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

context-events.mddocs/reference/

Context and Events

Context objects and event classes for MCP server operations, connection metadata, and change notifications.

Capabilities

McpConnectionInfo

Connection metadata record containing information about an MCP client connection.

/**
 * Connection metadata for an MCP client
 * Location: org.springframework.ai.mcp.McpConnectionInfo
 */
record McpConnectionInfo(
    McpSchema.ClientCapabilities clientCapabilities,
    McpSchema.Implementation clientInfo,
    McpSchema.InitializeResult initializeResult
) {
    /**
     * Create a builder for McpConnectionInfo
     */
    static Builder builder() { ... }

    interface Builder {
        /**
         * Set client capabilities
         */
        Builder clientCapabilities(McpSchema.ClientCapabilities capabilities);

        /**
         * Set client implementation info
         */
        Builder clientInfo(McpSchema.Implementation info);

        /**
         * Set initialization result
         */
        Builder initializeResult(McpSchema.InitializeResult result);

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

Usage Examples:

Used in filters and customizers:

import org.springframework.ai.mcp.McpConnectionInfo;
import org.springframework.ai.mcp.McpToolFilter;

@Bean
public McpToolFilter connectionAwareFilter() {
    return (McpConnectionInfo connectionInfo, Tool tool) -> {
        // Access client information
        String clientName = connectionInfo.clientInfo().name();
        String clientVersion = connectionInfo.clientInfo().version();

        // Access client capabilities
        boolean supportsSampling = connectionInfo.clientCapabilities().sampling() != null;

        // Filter based on connection metadata
        if ("legacy-client".equals(clientName)) {
            // Only allow basic tools for legacy clients
            return tool.name().startsWith("basic_");
        }

        return true; // Allow all tools for modern clients
    };
}

Building connection info:

McpConnectionInfo connectionInfo = McpConnectionInfo.builder()
    .clientCapabilities(new ClientCapabilities(
        new Experimental(),
        new Roots(true),
        new Sampling()
    ))
    .clientInfo(new Implementation("my-client", "1.0.0"))
    .initializeResult(new InitializeResult(
        "1.0",
        new Implementation("mcp-server", "1.1.2"),
        new ServerCapabilities(/* ... */)
    ))
    .build();

Used in tool name prefix generators:

@Bean
public McpToolNamePrefixGenerator customPrefixGenerator() {
    return (McpConnectionInfo connectionInfo, Tool tool) -> {
        // Use connection info to generate unique prefixes
        String clientName = connectionInfo.clientInfo().name();
        String toolTitle = tool.title() != null ? tool.title() : "";

        return McpToolUtils.format(clientName + "_" + toolTitle + "_" + tool.name());
    };
}

McpToolsChangedEvent

Spring application event published when MCP tools change, enabling automatic cache invalidation and tool list updates.

/**
 * Event published when tools from an MCP connection change
 * Location: org.springframework.ai.mcp.McpToolsChangedEvent
 * Extends: org.springframework.context.ApplicationEvent
 */
class McpToolsChangedEvent extends ApplicationEvent {
    /**
     * Create a tools changed event
     * @param connectionName The name of the connection
     * @param tools The updated list of tools
     */
    McpToolsChangedEvent(String connectionName, List<Tool> tools);

    /**
     * Get the connection name that changed
     */
    String getConnectionName();

    /**
     * Get the updated list of tools
     */
    List<Tool> getTools();
}

Usage Examples:

Publishing tool changes:

import org.springframework.ai.mcp.McpToolsChangedEvent;
import org.springframework.context.ApplicationEventPublisher;

@Component
public class ToolRegistry {
    private final ApplicationEventPublisher eventPublisher;

    public ToolRegistry(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void updateTools(String connectionName, List<Tool> newTools) {
        // Update tool registry
        toolMap.put(connectionName, newTools);

        // Publish event to notify listeners
        eventPublisher.publishEvent(
            new McpToolsChangedEvent(connectionName, newTools)
        );
    }
}

Listening for tool changes:

import org.springframework.ai.mcp.McpToolsChangedEvent;
import org.springframework.context.event.EventListener;

@Component
public class ToolCacheManager {

    @EventListener
    public void handleToolsChanged(McpToolsChangedEvent event) {
        String connectionName = event.getConnectionName();
        List<Tool> updatedTools = event.getTools();

        System.out.println("Tools changed for connection: " + connectionName);
        System.out.println("New tool count: " + updatedTools.size());

        // Invalidate cache for this connection
        toolCache.remove(connectionName);

        // Rebuild cache with new tools
        updatedTools.forEach(tool -> {
            toolCache.put(tool.name(), tool);
        });
    }
}

Automatic cache invalidation in tool callback providers:

// SyncMcpToolCallbackProvider and AsyncMcpToolCallbackProvider both
// implement ApplicationListener<McpToolsChangedEvent> automatically

@Bean
public SyncMcpToolCallbackProvider toolProvider(List<McpSyncClient> clients) {
    // This provider automatically listens for McpToolsChangedEvent
    // and invalidates its cache when tools change
    return SyncMcpToolCallbackProvider.builder()
        .mcpClients(clients)
        .build();
}

// When tools change, the provider automatically calls invalidateCache()
// internally, ensuring fresh tools are returned on next getToolCallbacks()

Custom event handling:

@Component
public class ToolMonitor implements ApplicationListener<McpToolsChangedEvent> {

    @Override
    public void onApplicationEvent(McpToolsChangedEvent event) {
        // Log tool changes for monitoring
        logger.info("Connection '{}' now has {} tools",
            event.getConnectionName(),
            event.getTools().size());

        // Send metrics
        metrics.gauge("mcp.tools.count",
            event.getTools().size(),
            "connection", event.getConnectionName());

        // Notify administrators
        if (event.getTools().isEmpty()) {
            alertService.send("No tools available for: " + event.getConnectionName());
        }
    }
}

Integration Patterns

Connection-Aware Filtering

@Configuration
public class FilterConfig {

    @Bean
    public McpToolFilter productionFilter() {
        return (connectionInfo, tool) -> {
            // Get environment from connection metadata
            String environment = connectionInfo.clientInfo().name();

            // Only allow safe tools in production
            if ("production".equals(environment)) {
                return !tool.annotations().destructiveHint();
            }

            return true; // Allow all in dev/test
        };
    }
}

Event-Driven Tool Updates

@Service
public class DynamicToolService {
    private final ApplicationEventPublisher eventPublisher;
    private final Map<String, List<Tool>> toolRegistry = new ConcurrentHashMap<>();

    @Scheduled(fixedRate = 60000) // Check every minute
    public void refreshTools() {
        for (String connectionName : getActiveConnections()) {
            List<Tool> freshTools = fetchToolsFromServer(connectionName);

            if (!Objects.equals(toolRegistry.get(connectionName), freshTools)) {
                // Tools changed - publish event
                toolRegistry.put(connectionName, freshTools);
                eventPublisher.publishEvent(
                    new McpToolsChangedEvent(connectionName, freshTools)
                );
            }
        }
    }
}

Connection Metadata in Customizers

@Bean
public McpSyncClientCustomizer connectionCustomizer() {
    return (String name, McpClient.SyncSpec spec) -> {
        // Customize client based on connection name
        if ("high-priority-server".equals(name)) {
            spec.timeout(Duration.ofSeconds(60));
        } else {
            spec.timeout(Duration.ofSeconds(30));
        }
    };
}

Core Imports

import org.springframework.ai.mcp.McpConnectionInfo;
import org.springframework.ai.mcp.McpToolsChangedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springaicommunity.mcp.schema.McpSchema.Tool;
import org.springaicommunity.mcp.schema.McpSchema.ClientCapabilities;
import org.springaicommunity.mcp.schema.McpSchema.Implementation;
import org.springaicommunity.mcp.schema.McpSchema.InitializeResult;
tessl i tessl/maven-org-springframework-ai--spring-ai-starter-mcp-server@1.1.0

docs

index.md

tile.json