Spring Framework integration for Model Context Protocol (MCP), providing Spring AI function calling capabilities and Spring-friendly abstractions for MCP clients and MCP servers
The spring-ai-mcp library is organized around three key architectural patterns that enable seamless integration between Spring AI and the Model Context Protocol (MCP).
Individual MCP tools are wrapped as Spring AI ToolCallback instances that handle the protocol conversion and execution. The library provides both synchronous and asynchronous implementations:
Each callback bridges the gap between MCP's tool representation and Spring AI's function calling interface, handling:
interface ToolCallback {
ToolDefinition getToolDefinition();
String call(String toolCallInput);
String call(String toolCallInput, ToolContext toolContext);
}class SyncMcpToolCallback implements ToolCallback {
// Blocking execution using McpSyncClient
String call(String toolCallInput, ToolContext toolContext);
}
class AsyncMcpToolCallback implements ToolCallback {
// Reactive execution using McpAsyncClient
// Blocks via .block() when call() is invoked
String call(String toolCallInput, ToolContext toolContext);
}Automatic discovery and management of multiple tools from one or more MCP servers. Providers implement the ToolCallbackProvider interface and handle:
The library provides:
interface ToolCallbackProvider {
ToolCallback[] getToolCallbacks();
}
interface ApplicationListener<McpToolsChangedEvent> {
void onApplicationEvent(McpToolsChangedEvent event);
}class SyncMcpToolCallbackProvider implements ToolCallbackProvider,
ApplicationListener<McpToolsChangedEvent> {
ToolCallback[] getToolCallbacks();
void invalidateCache();
void onApplicationEvent(McpToolsChangedEvent event);
static List<ToolCallback> syncToolCallbacks(List<McpSyncClient> mcpClients);
}
class AsyncMcpToolCallbackProvider implements ToolCallbackProvider,
ApplicationListener<McpToolsChangedEvent> {
ToolCallback[] getToolCallbacks();
void invalidateCache();
void onApplicationEvent(McpToolsChangedEvent event);
static Flux<ToolCallback> asyncToolCallbacks(List<McpAsyncClient> mcpClients);
}Helper classes and customization interfaces for advanced configuration:
McpToolUtils: Comprehensive utility class with 20+ static methods for:
Strategy Interfaces: Customization points for behavior:
McpToolFilter: Filter discovered tools based on custom criteriaMcpToolNamePrefixGenerator: Generate prefixed names to avoid conflictsToolContextToMcpMetaConverter: Convert Spring AI context to MCP metadataMcpSyncClientCustomizer / McpAsyncClientCustomizer: Customize client configurationsConnection Metadata: McpConnectionInfo record containing client capabilities, client info, and server initialization results, available to filters and generators
final class McpToolUtils {
// Tool name management
static String prefixedToolName(String prefix, String title, String toolName);
static String prefixedToolName(String prefix, String toolName);
static String format(String input);
// Tool definition creation
static ToolDefinition createToolDefinition(String prefixedToolName, McpSchema.Tool tool);
// Sync client integration
static List<ToolCallback> getToolCallbacksFromSyncClients(McpSyncClient... mcpClients);
static List<ToolCallback> getToolCallbacksFromSyncClients(List<McpSyncClient> mcpClients);
// Async client integration
static List<ToolCallback> getToolCallbacksFromAsyncClients(McpAsyncClient... asyncMcpClients);
static List<ToolCallback> getToolCallbacksFromAsyncClients(List<McpAsyncClient> asyncMcpClients);
// Server specifications
static List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecification(List<ToolCallback> toolCallbacks);
static List<McpServerFeatures.AsyncToolSpecification> toAsyncToolSpecifications(List<ToolCallback> toolCallbacks);
// Context access
static Optional<McpSyncServerExchange> getMcpExchange(ToolContext toolContext);
// Constants
static final String TOOL_CONTEXT_MCP_EXCHANGE_KEY = "exchange";
}@FunctionalInterface
interface McpToolFilter extends BiPredicate<McpConnectionInfo, McpSchema.Tool> {
boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool);
}
interface McpToolNamePrefixGenerator {
String prefixedToolName(McpConnectionInfo mcpConnectionInfo, McpSchema.Tool tool);
static McpToolNamePrefixGenerator noPrefix();
}
@FunctionalInterface
interface ToolContextToMcpMetaConverter {
Map<String, Object> convert(ToolContext toolContext);
static ToolContextToMcpMetaConverter defaultConverter();
static ToolContextToMcpMetaConverter noOp();
}User Request → Spring AI ChatClient → SyncMcpToolCallback
↓ (blocking)
McpSyncClient
↓ (blocking)
MCP Server
↓
ResponseBest for:
Characteristics:
User Request → Spring AI ChatClient → AsyncMcpToolCallback
↓ (reactive)
McpAsyncClient (Mono)
↓ (non-blocking)
MCP Server
↓ (reactive)
Response (via .block())Best for:
Characteristics:
Note: While AsyncMcpToolCallback uses reactive execution internally, the call() method blocks using .block() to satisfy the ToolCallback interface contract. For fully non-blocking operations, use the underlying McpAsyncClient directly or the AsyncMcpToolCallbackProvider.asyncToolCallbacks() factory method which returns a Flux<ToolCallback>.
The library integrates with Spring's event system for dynamic tool updates:
MCP Server Tools Changed
↓
McpToolsChangedEvent published
↓
ToolCallbackProvider receives event
↓
Cache invalidated
↓
Next getToolCallbacks() re-discovers toolsThis enables:
// Event class
class McpToolsChangedEvent extends ApplicationEvent {
McpToolsChangedEvent(String connectionName, List<McpSchema.Tool> tools);
String getConnectionName();
List<McpSchema.Tool> getTools();
}
// Publisher (your code)
applicationEventPublisher.publishEvent(
new McpToolsChangedEvent("serverName", tools)
);
// Listener (automatic in providers)
void onApplicationEvent(McpToolsChangedEvent event) {
this.invalidateCache();
}All major classes use the Builder pattern for flexible, safe object construction:
// SyncMcpToolCallback.Builder
class Builder {
Builder mcpClient(McpSyncClient mcpClient); // required
Builder tool(McpSchema.Tool tool); // required
Builder prefixedToolName(String prefixedToolName); // optional
Builder toolContextToMcpMetaConverter(ToolContextToMcpMetaConverter converter); // optional
SyncMcpToolCallback build();
}
// SyncMcpToolCallbackProvider.Builder
class Builder {
Builder mcpClients(List<McpSyncClient> mcpClients); // required
Builder mcpClients(McpSyncClient... mcpClients); // alternative
Builder addMcpClient(McpSyncClient mcpClient); // alternative
Builder toolFilter(McpToolFilter toolFilter); // optional
Builder toolNamePrefixGenerator(McpToolNamePrefixGenerator generator); // optional
Builder toolContextToMcpMetaConverter(ToolContextToMcpMetaConverter converter); // optional
SyncMcpToolCallbackProvider build();
}
// McpConnectionInfo.Builder
class Builder {
Builder clientCapabilities(McpSchema.ClientCapabilities clientCapabilities);
Builder clientInfo(McpSchema.Implementation clientInfo);
Builder initializeResult(McpSchema.InitializeResult initializeResult);
McpConnectionInfo build();
}MCP Servers → MCP Clients → ToolCallbackProvider → Spring AI ChatClient → LLMThe library discovers tools from MCP servers and makes them available to language models through Spring AI's function calling mechanism.
listTools() called on each clientMcpToolFilterMcpToolNamePrefixGeneratorToolCallback instancesChatClientSpring AI Functions → ToolCallback → McpToolUtils → MCP Server Features → MCP ClientsThe library converts Spring AI tool callbacks into MCP server specifications, allowing Spring functions to be exposed as MCP tools.
ToolCallback beansMcpToolUtils.toSyncToolSpecification() or toAsyncToolSpecification()Tool Callbacks: Immutable after construction, fully thread-safe
Tool Callback Providers: Thread-safe caching with proper synchronization
ReentrantLock for cache accessgetToolCallbacks() callsDefaultMcpToolNamePrefixGenerator: Thread-safe with concurrent hash map for connection tracking
ConcurrentHashMap for connection trackingMcpToolUtils: All methods are static and stateless, inherently thread-safe
The library employs several design patterns:
Adapter Pattern: Tool callbacks adapt MCP tools to Spring AI interface
SyncMcpToolCallback adapts McpSyncClient to ToolCallbackAsyncMcpToolCallback adapts McpAsyncClient to ToolCallbackBuilder Pattern: Fluent construction of complex objects
Strategy Pattern: Pluggable filters, generators, and converters
McpToolFilter for tool filteringMcpToolNamePrefixGenerator for name generationToolContextToMcpMetaConverter for context conversionProvider Pattern: Discovery and supply of tool callbacks
ToolCallbackProvider interfaceFactory Pattern: Static factory methods for creating instances
McpToolNamePrefixGenerator.noPrefix()ToolContextToMcpMetaConverter.defaultConverter()SyncMcpToolCallbackProvider.syncToolCallbacks()Observer Pattern: Event listeners for tool changes
ApplicationEventPublisher / ApplicationListenerMcpToolsChangedEvent for notificationsFacade Pattern: McpToolUtils provides simplified interface to complex operations
The library includes McpHints runtime hints registrar for GraalVM native image compilation:
class McpHints implements RuntimeHintsRegistrar {
void registerHints(RuntimeHints hints, ClassLoader classLoader);
}This ensures the library works seamlessly in:
Registration includes:
McpSchema nested classesThe architecture is designed for extensibility:
Custom Filters: Implement McpToolFilter for domain-specific tool selection
McpToolFilter custom = (info, tool) -> /* custom logic */;Custom Naming: Implement McpToolNamePrefixGenerator for custom naming schemes
McpToolNamePrefixGenerator custom = (info, tool) -> /* custom prefix */;Custom Conversion: Implement ToolContextToMcpMetaConverter for context handling
ToolContextToMcpMetaConverter custom = (context) -> /* custom metadata */;Client Customization: Use customizer interfaces to configure MCP clients
McpSyncClientCustomizer customizer = (name, spec) -> /* configure spec */;RuntimeException
└─ ToolExecutionException
// Wraps all tool execution errors
// Contains ToolDefinition for contextTool Call
↓
Try Execute
↓
MCP Client Error → ToolExecutionException
↓
MCP Server Error → ToolExecutionException
↓
JSON Parse Error → ToolExecutionException
↓
Return to CallerAll errors are wrapped in ToolExecutionException with the tool definition for context.