CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-ai--spring-ai-mcp

Spring Framework integration for Model Context Protocol (MCP), providing Spring AI function calling capabilities and Spring-friendly abstractions for MCP clients and MCP servers

Overview
Eval results
Files

architecture.mddocs/reference/

Architecture

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).

Core Architectural Components

1. Tool Callbacks

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:

  • SyncMcpToolCallback: Blocking execution model for simple, short-running operations
  • AsyncMcpToolCallback: Reactive execution model using Project Reactor for long-running or non-blocking operations

Each callback bridges the gap between MCP's tool representation and Spring AI's function calling interface, handling:

  • Tool definition conversion from MCP schema to Spring AI format
  • Input/output JSON transformation
  • Error handling and exception mapping
  • Context propagation (standard Java for sync, reactive for async)

Tool Callback Interface

interface ToolCallback {
    ToolDefinition getToolDefinition();
    String call(String toolCallInput);
    String call(String toolCallInput, ToolContext toolContext);
}

Implementation Classes

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);
}

2. Tool Callback Providers

Automatic discovery and management of multiple tools from one or more MCP servers. Providers implement the ToolCallbackProvider interface and handle:

  • Tool Discovery: Automatically query MCP servers for available tools
  • Caching: Cache discovered tools for performance, with manual and event-driven invalidation
  • Filtering: Apply custom predicates to include/exclude specific tools
  • Naming Strategy: Generate unique prefixed names to avoid collisions across servers
  • Lifecycle Management: React to tool changes through Spring's event system

The library provides:

  • SyncMcpToolCallbackProvider: Synchronous tool discovery with blocking operations
  • AsyncMcpToolCallbackProvider: Reactive tool discovery using Project Reactor

Provider Interface

interface ToolCallbackProvider {
    ToolCallback[] getToolCallbacks();
}

interface ApplicationListener<McpToolsChangedEvent> {
    void onApplicationEvent(McpToolsChangedEvent event);
}

Implementation Classes

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);
}

3. Utility and Strategy Components

Helper classes and customization interfaces for advanced configuration:

  • McpToolUtils: Comprehensive utility class with 20+ static methods for:

    • Tool name formatting and prefixing
    • Tool definition creation
    • Conversion between Spring AI and MCP representations
    • Server feature specification generation
    • Quick tool discovery convenience methods
  • Strategy Interfaces: Customization points for behavior:

    • McpToolFilter: Filter discovered tools based on custom criteria
    • McpToolNamePrefixGenerator: Generate prefixed names to avoid conflicts
    • ToolContextToMcpMetaConverter: Convert Spring AI context to MCP metadata
    • McpSyncClientCustomizer / McpAsyncClientCustomizer: Customize client configurations
  • Connection Metadata: McpConnectionInfo record containing client capabilities, client info, and server initialization results, available to filters and generators

Utility Class

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";
}

Strategy Interfaces

@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();
}

Execution Models

Synchronous Execution

User Request → Spring AI ChatClient → SyncMcpToolCallback
                                            ↓ (blocking)
                                       McpSyncClient
                                            ↓ (blocking)
                                       MCP Server
                                            ↓
                                       Response

Best for:

  • Simple, short-running operations (< 5 seconds)
  • Traditional blocking applications
  • Scenarios where immediate results are expected
  • REST APIs with short timeouts
  • Command-line tools
  • Batch processing with sequential operations

Characteristics:

  • Thread Model: One thread per request
  • Resource Usage: Thread blocked during execution
  • Error Handling: Synchronous exceptions
  • Timeout: Blocking with configurable timeout
  • Backpressure: Not applicable

Asynchronous Execution

User Request → Spring AI ChatClient → AsyncMcpToolCallback
                                            ↓ (reactive)
                                       McpAsyncClient (Mono)
                                            ↓ (non-blocking)
                                       MCP Server
                                            ↓ (reactive)
                                       Response (via .block())

Best for:

  • Long-running operations (> 5 seconds)
  • Reactive Spring applications (WebFlux)
  • Scenarios requiring backpressure handling
  • High-concurrency environments
  • Microservices with async communication
  • Event-driven architectures

Characteristics:

  • Thread Model: Event loop / scheduler threads
  • Resource Usage: Non-blocking, efficient thread usage
  • Error Handling: Reactive error propagation
  • Timeout: Reactive timeout operators
  • Backpressure: Full support via Mono/Flux

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>.

Event-Driven Architecture

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 tools

This enables:

  • Dynamic tool discovery without restart
  • Automatic updates when MCP servers change
  • Manual cache invalidation when needed
  • Multiple listeners for tool change notifications

Event Flow

// 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();
}

Builder Pattern Architecture

All major classes use the Builder pattern for flexible, safe object construction:

  • Type-safe construction: Required parameters enforced at build time
  • Immutable results: All built objects are immutable and thread-safe
  • Sensible defaults: Optional parameters have reasonable default values
  • Fluent API: Chainable method calls for readable configuration

Builder Pattern Examples

// 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();
}

Integration Points

Client Side (Consuming MCP Tools)

MCP Servers → MCP Clients → ToolCallbackProvider → Spring AI ChatClient → LLM

The library discovers tools from MCP servers and makes them available to language models through Spring AI's function calling mechanism.

Integration Flow

  1. MCP Clients connect to one or more MCP servers
  2. Tool Discovery: listTools() called on each client
  3. Filtering: Optional filtering via McpToolFilter
  4. Naming: Prefixes applied via McpToolNamePrefixGenerator
  5. Wrapping: Tools wrapped as ToolCallback instances
  6. Registration: Callbacks registered with Spring AI ChatClient
  7. Execution: LLM invokes tools through Spring AI's function calling

Server Side (Exposing Spring Functions as MCP Tools)

Spring AI Functions → ToolCallback → McpToolUtils → MCP Server Features → MCP Clients

The library converts Spring AI tool callbacks into MCP server specifications, allowing Spring functions to be exposed as MCP tools.

Integration Flow

  1. Spring Functions defined as ToolCallback beans
  2. Conversion: McpToolUtils.toSyncToolSpecification() or toAsyncToolSpecification()
  3. Server Creation: MCP server built with tool specifications
  4. Transport: Server exposed via HTTP/WebSocket/stdin
  5. Client Connection: External MCP clients connect
  6. Tool Invocation: Clients call tools, server executes Spring functions

Thread Safety

  • Tool Callbacks: Immutable after construction, fully thread-safe

    • Safe for concurrent invocation from multiple threads
    • No shared mutable state
    • Underlying MCP client must be thread-safe
  • Tool Callback Providers: Thread-safe caching with proper synchronization

    • Uses ReentrantLock for cache access
    • Safe for concurrent getToolCallbacks() calls
    • Cache invalidation is atomic
  • DefaultMcpToolNamePrefixGenerator: Thread-safe with concurrent hash map for connection tracking

    • Uses ConcurrentHashMap for connection tracking
    • Safe for concurrent prefix generation
  • McpToolUtils: All methods are static and stateless, inherently thread-safe

Design Patterns

The library employs several design patterns:

  1. Adapter Pattern: Tool callbacks adapt MCP tools to Spring AI interface

    • SyncMcpToolCallback adapts McpSyncClient to ToolCallback
    • AsyncMcpToolCallback adapts McpAsyncClient to ToolCallback
  2. Builder Pattern: Fluent construction of complex objects

    • All major classes provide builder interfaces
    • Required vs. optional parameters clearly distinguished
  3. Strategy Pattern: Pluggable filters, generators, and converters

    • McpToolFilter for tool filtering
    • McpToolNamePrefixGenerator for name generation
    • ToolContextToMcpMetaConverter for context conversion
  4. Provider Pattern: Discovery and supply of tool callbacks

    • ToolCallbackProvider interface
    • Sync and async implementations
  5. Factory Pattern: Static factory methods for creating instances

    • McpToolNamePrefixGenerator.noPrefix()
    • ToolContextToMcpMetaConverter.defaultConverter()
    • SyncMcpToolCallbackProvider.syncToolCallbacks()
  6. Observer Pattern: Event listeners for tool changes

    • Spring's ApplicationEventPublisher / ApplicationListener
    • McpToolsChangedEvent for notifications
  7. Facade Pattern: McpToolUtils provides simplified interface to complex operations

    • Single entry point for common operations
    • Simplifies client integration

AOT and Native Image Support

The library includes McpHints runtime hints registrar for GraalVM native image compilation:

  • Registers all MCP schema classes for reflection
  • Enables serialization/deserialization in native images
  • Supports Spring AOT processing for ahead-of-time compilation
class McpHints implements RuntimeHintsRegistrar {
    void registerHints(RuntimeHints hints, ClassLoader classLoader);
}

This ensures the library works seamlessly in:

  • Traditional JVM applications
  • Spring Native applications
  • GraalVM native executables

Registration includes:

  • All McpSchema nested classes
  • JSON-B annotations for serialization
  • Constructor reflection hints
  • Field reflection hints

Extensibility

The 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 */;

Error Handling Architecture

Exception Hierarchy

RuntimeException
    └─ ToolExecutionException
        // Wraps all tool execution errors
        // Contains ToolDefinition for context

Error Flow

Tool Call
    ↓
Try Execute
    ↓
MCP Client Error → ToolExecutionException
    ↓
MCP Server Error → ToolExecutionException
    ↓
JSON Parse Error → ToolExecutionException
    ↓
Return to Caller

Error Categories

  1. Client Errors: Connection failures, timeouts
  2. Server Errors: Tool execution failures, validation errors
  3. Protocol Errors: Invalid JSON, schema violations
  4. Application Errors: Custom tool logic errors

All errors are wrapped in ToolExecutionException with the tool definition for context.

Performance Characteristics

Memory Usage

  • Tool Callbacks: ~1KB per callback (immutable, cached)
  • Providers: ~10KB + (number of tools × 1KB)
  • Cache: Tool definitions cached in memory
  • Native Image: 50-70% reduction in memory vs. JVM

Latency

  • Tool Discovery: 100-500ms per server (cached)
  • Sync Tool Call: Network latency + execution time (blocking)
  • Async Tool Call: Network latency + execution time (non-blocking)
  • Cache Hit: < 1ms for getToolCallbacks()

Scalability

  • Concurrent Calls: Thread-safe for unlimited concurrent calls
  • Number of Tools: Linear scaling O(n) for discovery
  • Number of Servers: Linear scaling O(n) for multi-server
  • Cache Size: Proportional to number of unique tools

Related Components

  • Synchronous Tool Callbacks - Sync execution details
  • Asynchronous Tool Callbacks - Async execution details
  • Synchronous Tool Discovery - Sync provider details
  • Asynchronous Tool Discovery - Async provider details
  • Tool Filtering and Naming - Customization strategies
  • Event Handling - Event-driven updates

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-ai--spring-ai-mcp

docs

index.md

tile.json