CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

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

Spring Boot Starter for building Model Context Protocol (MCP) servers with auto-configuration, annotation-based tool/resource/prompt definitions, and support for STDIO, SSE, and Streamable-HTTP transports

Overview
Eval results
Files

tool-callbacks.mddocs/reference/

Tool Callbacks

Tool Callbacks provide a programmatic approach for registering MCP tools using Spring AI's ToolCallback interface. This approach offers more control than annotations and is useful for dynamic tool registration or integrating with existing ToolCallback implementations.

Capabilities

AsyncMcpToolCallback

Adapts MCP tools to Spring AI's ToolCallback interface with asynchronous execution.

/**
 * Asynchronous adapter for MCP tools to Spring AI ToolCallback interface
 */
class AsyncMcpToolCallback implements ToolCallback {
    /**
     * Create a builder for AsyncMcpToolCallback
     */
    static Builder builder() { ... }

    /**
     * Get the tool definition for this callback
     */
    ToolDefinition getToolDefinition();

    /**
     * Get the original MCP tool name
     */
    String getOriginalToolName();

    /**
     * Execute the tool synchronously with input
     */
    String call(String toolCallInput);

    /**
     * Execute the tool with input and context
     */
    String call(String toolCallInput, ToolContext toolContext);

    interface Builder {
        /**
         * Set the MCP async client
         */
        Builder mcpClient(McpAsyncClient mcpClient);

        /**
         * Set the MCP tool
         */
        Builder tool(Tool tool);

        /**
         * Set the prefixed tool name (optional)
         */
        Builder prefixedToolName(String prefixedToolName);

        /**
         * Set the tool context to MCP metadata converter (optional)
         */
        Builder toolContextToMcpMetaConverter(ToolContextToMcpMetaConverter converter);

        /**
         * Build the AsyncMcpToolCallback instance
         */
        AsyncMcpToolCallback build();
    }
}

Usage Example:

import org.springframework.ai.mcp.AsyncMcpToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springaicommunity.mcp.client.McpAsyncClient;
import org.springaicommunity.mcp.schema.McpSchema.Tool;

@Configuration
public class ToolCallbackConfig {

    @Bean
    public AsyncMcpToolCallback weatherTool(McpAsyncClient mcpClient) {
        Tool weatherTool = new Tool(
            "get_weather",
            "Get current weather for a location",
            createWeatherSchema()
        );

        return AsyncMcpToolCallback.builder()
            .mcpClient(mcpClient)
            .tool(weatherTool)
            .prefixedToolName("weather_service_get_weather")
            .build();
    }

    private Map<String, Object> createWeatherSchema() {
        // JSON schema for tool parameters
        return Map.of(
            "type", "object",
            "properties", Map.of(
                "location", Map.of("type", "string", "description", "City name")
            ),
            "required", List.of("location")
        );
    }
}

SyncMcpToolCallback

Synchronous adapter for MCP tools to Spring AI's ToolCallback interface.

/**
 * Synchronous adapter for MCP tools to Spring AI ToolCallback interface
 */
class SyncMcpToolCallback implements ToolCallback {
    /**
     * Create a builder for SyncMcpToolCallback
     */
    static Builder builder() { ... }

    /**
     * Get the tool definition for this callback
     */
    ToolDefinition getToolDefinition();

    /**
     * Get the original MCP tool name
     */
    String getOriginalToolName();

    /**
     * Execute the tool with input
     */
    String call(String toolCallInput);

    /**
     * Execute the tool with input and context
     */
    String call(String toolCallInput, ToolContext toolContext);

    interface Builder {
        /**
         * Set the MCP sync client
         */
        Builder mcpClient(McpSyncClient mcpClient);

        /**
         * Set the MCP tool
         */
        Builder tool(Tool tool);

        /**
         * Set the prefixed tool name (optional)
         */
        Builder prefixedToolName(String prefixedToolName);

        /**
         * Set the tool context to MCP metadata converter (optional)
         */
        Builder toolContextToMcpMetaConverter(ToolContextToMcpMetaConverter converter);

        /**
         * Build the SyncMcpToolCallback instance
         */
        SyncMcpToolCallback build();
    }
}

Usage Example:

@Configuration
public class SyncToolConfig {

    @Bean
    public SyncMcpToolCallback calculatorTool(McpSyncClient mcpClient) {
        Tool addTool = new Tool(
            "add",
            "Add two numbers",
            Map.of(
                "type", "object",
                "properties", Map.of(
                    "a", Map.of("type", "number"),
                    "b", Map.of("type", "number")
                ),
                "required", List.of("a", "b")
            )
        );

        return SyncMcpToolCallback.builder()
            .mcpClient(mcpClient)
            .tool(addTool)
            .build();
    }
}

AsyncMcpToolCallbackProvider

Provides MCP tools asynchronously from multiple MCP servers as Spring AI tool callbacks.

/**
 * Provides MCP tools from multiple servers asynchronously
 */
class AsyncMcpToolCallbackProvider {
    /**
     * Create a builder for AsyncMcpToolCallbackProvider
     */
    static Builder builder() { ... }

    /**
     * Get all available tool callbacks
     * Discovers tools from configured MCP clients
     */
    ToolCallback[] getToolCallbacks();

    /**
     * Invalidate cache and force re-discovery on next request
     */
    void invalidateCache();

    /**
     * Create a reactive stream of tool callbacks from clients
     */
    static Flux<ToolCallback> asyncToolCallbacks(List<McpAsyncClient> clients) { ... }

    interface Builder {
        /**
         * Set the tool filter for selective tool discovery
         */
        Builder toolFilter(McpToolFilter filter);

        /**
         * Set the MCP clients (list)
         */
        Builder mcpClients(List<McpAsyncClient> clients);

        /**
         * Set the MCP clients (varargs)
         */
        Builder mcpClients(McpAsyncClient... clients);

        /**
         * Set the tool name prefix generator
         */
        Builder toolNamePrefixGenerator(McpToolNamePrefixGenerator generator);

        /**
         * Set the tool context to MCP metadata converter
         */
        Builder toolContextToMcpMetaConverter(ToolContextToMcpMetaConverter converter);

        /**
         * Build the AsyncMcpToolCallbackProvider instance
         */
        AsyncMcpToolCallbackProvider build();
    }
}

Usage Example:

@Configuration
public class ToolProviderConfig {

    @Bean
    public AsyncMcpToolCallbackProvider toolProvider(
            List<McpAsyncClient> mcpClients) {

        return AsyncMcpToolCallbackProvider.builder()
            .mcpClients(mcpClients)
            .toolFilter((connectionInfo, tool) -> {
                // Filter out certain tools
                return !tool.name().startsWith("internal_");
            })
            .toolNamePrefixGenerator((connectionInfo, tool) -> {
                // Generate unique prefixed names
                String serverName = connectionInfo.clientInfo().name();
                return serverName + "_" + tool.name();
            })
            .build();
    }
}

SyncMcpToolCallbackProvider

Provides MCP tools synchronously from MCP servers as Spring AI tool callbacks.

/**
 * Provides MCP tools from multiple servers synchronously
 */
class SyncMcpToolCallbackProvider {
    /**
     * Create a builder for SyncMcpToolCallbackProvider
     */
    static Builder builder() { ... }

    /**
     * Get all available tool callbacks
     */
    ToolCallback[] getToolCallbacks();

    /**
     * Invalidate cache and force re-discovery
     */
    void invalidateCache();

    /**
     * Consolidate tools from multiple sync clients
     */
    static List<ToolCallback> syncToolCallbacks(List<McpSyncClient> clients) { ... }

    interface Builder {
        /**
         * Set the MCP clients (list)
         */
        Builder mcpClients(List<McpSyncClient> clients);

        /**
         * Set the MCP clients (varargs)
         */
        Builder mcpClients(McpSyncClient... clients);

        /**
         * Add a single MCP client
         */
        Builder addMcpClient(McpSyncClient client);

        /**
         * Set the tool filter
         */
        Builder toolFilter(McpToolFilter filter);

        /**
         * Set the tool name prefix generator
         */
        Builder toolNamePrefixGenerator(McpToolNamePrefixGenerator generator);

        /**
         * Set the tool context to MCP metadata converter
         */
        Builder toolContextToMcpMetaConverter(ToolContextToMcpMetaConverter converter);

        /**
         * Build the SyncMcpToolCallbackProvider instance
         */
        SyncMcpToolCallbackProvider build();
    }
}

Usage Example:

@Configuration
public class SyncProviderConfig {

    @Bean
    public SyncMcpToolCallbackProvider syncToolProvider(McpSyncClient client1, McpSyncClient client2) {
        return SyncMcpToolCallbackProvider.builder()
            .mcpClients(client1, client2)
            .toolFilter((connectionInfo, tool) -> tool.description() != null)
            .toolNamePrefixGenerator(McpToolNamePrefixGenerator.noPrefix())
            .build();
    }
}

Strategy Interfaces

McpToolFilter

Filter discovered tools by MCP connection and tool metadata.

/**
 * BiPredicate for filtering discovered tools
 */
@FunctionalInterface
interface McpToolFilter extends BiPredicate<McpConnectionInfo, McpSchema.Tool> {
    /**
     * Test whether a tool should be included
     * @param connectionInfo Information about the MCP connection
     * @param tool The MCP tool
     * @return true to include the tool, false to filter it out
     */
    boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool);
}

Usage Examples:

Filter by tool name:

McpToolFilter nameFilter = (connectionInfo, tool) ->
    tool.name().startsWith("approved_");

Filter by description:

McpToolFilter descriptionFilter = (connectionInfo, tool) ->
    tool.description() != null && !tool.description().isEmpty();

Filter by connection:

McpToolFilter connectionFilter = (connectionInfo, tool) -> {
    String serverName = connectionInfo.clientInfo().name();
    return "trusted-server".equals(serverName);
};

McpToolNamePrefixGenerator

Strategy for generating prefixed tool names to avoid conflicts.

/**
 * Strategy interface for generating prefixed tool names
 */
interface McpToolNamePrefixGenerator {
    /**
     * Generate a prefixed tool name
     * @param connectionInfo Information about the MCP connection
     * @param tool The MCP tool
     * @return Prefixed tool name
     */
    String prefixedToolName(McpConnectionInfo connectionInfo, McpSchema.Tool tool);

    /**
     * Factory method: no prefix (returns tool name as-is)
     */
    static McpToolNamePrefixGenerator noPrefix() { ... }
}
/**
 * Default implementation ensuring unique tool names
 */
class DefaultMcpToolNamePrefixGenerator implements McpToolNamePrefixGenerator {
    /**
     * Generate unique prefixed tool name with conflict resolution
     * Format: {serverName}_{toolName} or {serverName}_{toolName}_{number} if conflict
     */
    @Override
    String prefixedToolName(McpConnectionInfo connectionInfo, Tool tool);
}

Usage Examples:

Default implementation (uses server name):

McpToolNamePrefixGenerator defaultGenerator = (connectionInfo, tool) -> {
    String serverName = connectionInfo.clientInfo().name();
    return serverName + "_" + tool.name();
};

Custom prefix:

McpToolNamePrefixGenerator customGenerator = (connectionInfo, tool) -> {
    String version = connectionInfo.clientInfo().version();
    return "v" + version + "_" + tool.name();
};

No prefix:

McpToolNamePrefixGenerator noPrefixGenerator = McpToolNamePrefixGenerator.noPrefix();

Use default implementation:

McpToolNamePrefixGenerator defaultGenerator = new DefaultMcpToolNamePrefixGenerator();

ToolContextToMcpMetaConverter

Strategy for converting Spring AI ToolContext to MCP metadata.

/**
 * Strategy for converting ToolContext to MCP metadata map
 */
interface ToolContextToMcpMetaConverter {
    /**
     * Convert ToolContext to MCP metadata map
     * @param toolContext The Spring AI ToolContext
     * @return Map of metadata for MCP
     */
    Map<String, Object> convert(ToolContext toolContext);

    /**
     * Factory method: default converter
     */
    static ToolContextToMcpMetaConverter defaultConverter() { ... }

    /**
     * Factory method: no-op converter (empty map)
     */
    static ToolContextToMcpMetaConverter noOp() { ... }
}

Usage Examples:

Default converter:

ToolContextToMcpMetaConverter defaultConverter = ToolContextToMcpMetaConverter.defaultConverter();

Custom converter:

ToolContextToMcpMetaConverter customConverter = (toolContext) -> {
    Map<String, Object> meta = new HashMap<>();
    meta.put("timestamp", System.currentTimeMillis());
    meta.put("user", toolContext.getContext().get("user"));
    return meta;
};

No-op converter:

ToolContextToMcpMetaConverter noOpConverter = ToolContextToMcpMetaConverter.noOp();

Supporting Types

McpConnectionInfo

Container for MCP connection metadata.

/**
 * Record containing MCP connection metadata
 */
record McpConnectionInfo(
    ClientCapabilities clientCapabilities,
    Implementation clientInfo,
    InitializeResult initializeResult
) {
    /**
     * Builder for McpConnectionInfo
     */
    static Builder builder() { ... }

    interface Builder {
        Builder clientCapabilities(McpSchema.ClientCapabilities capabilities);
        Builder clientInfo(McpSchema.Implementation info);
        Builder initializeResult(McpSchema.InitializeResult result);
        McpConnectionInfo build();
    }
}

Integration with Spring AI

Tool callbacks are automatically discovered and registered when defined as Spring beans:

@Configuration
public class ToolConfiguration {

    // Individual callback
    @Bean
    public SyncMcpToolCallback myTool(McpSyncClient client) {
        return SyncMcpToolCallback.builder()
            .mcpClient(client)
            .tool(createTool())
            .build();
    }

    // List of callbacks
    @Bean
    public List<ToolCallback> myTools(McpSyncClient client) {
        return List.of(
            createToolCallback1(client),
            createToolCallback2(client),
            createToolCallback3(client)
        );
    }

    // Tool callback provider
    @Bean
    public SyncMcpToolCallbackProvider toolProvider(List<McpSyncClient> clients) {
        return SyncMcpToolCallbackProvider.builder()
            .mcpClients(clients)
            .build();
    }

    // Spring AI ToolCallbackProvider
    @Bean
    public ToolCallbackProvider aiToolProvider(List<ToolCallback> callbacks) {
        return ToolCallbackProvider.from(callbacks);
    }
}

Disabling Automatic Tool Callback Conversion

To disable automatic conversion of ToolCallbacks to MCP specifications:

spring.ai.mcp.server.tool-callback-converter=false

This is useful when you want full control over tool registration using low-level MCP specifications.

Tool Discovery Flow

  1. Bean Registration: Tool callbacks are registered as Spring beans
  2. Auto-Discovery: Auto-configuration detects ToolCallback beans, lists, and providers
  3. Conversion: Callbacks are converted to MCP tool specifications (if enabled)
  4. Deduplication: Tools are deduplicated by name (first occurrence wins)
  5. Registration: Tools are registered with the MCP server

Comparison: Annotations vs Tool Callbacks

FeatureAnnotationsTool Callbacks
Simplicity✓ Very simpleMore verbose
Type Safety✓ Compile-timeRuntime
Dynamic Registration✗ Static✓ Dynamic
Schema ControlAutomaticManual
IntegrationDirectVia ToolCallback interface
FlexibilityLimitedHigh
Use CaseMost applicationsComplex integrations, dynamic tools

Core Imports

// Tool callback classes (server-side)
import org.springframework.ai.mcp.AsyncMcpToolCallback;
import org.springframework.ai.mcp.SyncMcpToolCallback;
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;

// Strategy interfaces
import org.springframework.ai.mcp.McpToolFilter;
import org.springframework.ai.mcp.McpToolNamePrefixGenerator;
import org.springframework.ai.mcp.DefaultMcpToolNamePrefixGenerator;
import org.springframework.ai.mcp.ToolContextToMcpMetaConverter;

// Connection metadata
import org.springframework.ai.mcp.McpConnectionInfo;

// Spring AI tool interfaces
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.ToolDefinition;
import org.springframework.ai.tool.ToolContext;

// MCP Schema types
import org.springaicommunity.mcp.schema.McpSchema.Tool;

// MCP client types
import org.springaicommunity.mcp.client.McpSyncClient;
import org.springaicommunity.mcp.client.McpAsyncClient;

// Events
import org.springframework.ai.mcp.McpToolsChangedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;

// Reactive types
import reactor.core.publisher.Flux;

// Java standard library
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
tessl i tessl/maven-org-springframework-ai--spring-ai-starter-mcp-server@1.1.0

docs

index.md

tile.json