Spring Framework integration for Model Context Protocol (MCP), providing Spring AI function calling capabilities and Spring-friendly abstractions for MCP clients and MCP servers
Customize MCP client configuration for both synchronous and asynchronous clients through customizer interfaces.
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
import io.modelcontextprotocol.client.McpClient;@FunctionalInterface
public interface McpSyncClientCustomizer {
void customize(String name, McpClient.SyncSpec spec);
}Functional interface for customizing synchronous MCP client specifications before they are built.
McpClient.SyncSpec to customizeimport org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.stereotype.Component;
@Component
public class CustomSyncClientCustomizer implements McpSyncClientCustomizer {
@Override
public void customize(String name, McpClient.SyncSpec spec) {
System.out.println("Customizing sync client: " + name);
// Add custom configuration to the sync spec
// The McpClient.SyncSpec provides methods for configuring
// the synchronous MCP client
}
}@FunctionalInterface
public interface McpAsyncClientCustomizer {
void customize(String name, McpClient.AsyncSpec spec);
}Functional interface for customizing asynchronous MCP client specifications before they are built.
McpClient.AsyncSpec to customizeimport org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
import org.springframework.stereotype.Component;
@Component
public class CustomAsyncClientCustomizer implements McpAsyncClientCustomizer {
@Override
public void customize(String name, McpClient.AsyncSpec spec) {
System.out.println("Customizing async client: " + name);
// Add custom configuration to the async spec
// The McpClient.AsyncSpec provides methods for configuring
// the asynchronous MCP client
}
}McpSyncClientCustomizer syncCustomizer = (name, spec) -> {
if (name.contains("production")) {
// Production-specific configuration
System.out.println("Configuring production sync client: " + name);
// Apply production settings to spec
} else {
// Development configuration
System.out.println("Configuring development sync client: " + name);
// Apply development settings to spec
}
};McpAsyncClientCustomizer asyncCustomizer = (name, spec) -> {
if (name.contains("high-latency")) {
// Configure for high-latency environments
System.out.println("Configuring async client for high latency: " + name);
// Adjust timeouts, retry policies, etc.
}
};import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
@Configuration
public class McpSyncCustomizerConfig {
@Bean
public McpSyncClientCustomizer productionSyncCustomizer() {
return (name, spec) -> {
// Global customization for all sync clients
System.out.println("Applying global sync customization for: " + name);
// Configure connection settings
// Configure timeouts
// Configure security settings
// etc.
};
}
@Bean
public McpSyncClientCustomizer environmentSpecificCustomizer() {
String environment = System.getenv("ENVIRONMENT");
return (name, spec) -> {
if ("production".equals(environment)) {
// Production settings
} else if ("staging".equals(environment)) {
// Staging settings
} else {
// Development settings
}
};
}
}import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
@Configuration
public class McpAsyncCustomizerConfig {
@Bean
public McpAsyncClientCustomizer reactiveCustomizer() {
return (name, spec) -> {
System.out.println("Customizing async client: " + name);
// Configure reactive settings
// Configure schedulers
// Configure backpressure
// etc.
};
}
}import org.springframework.ai.mcp.customizer.*;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;
import io.modelcontextprotocol.client.McpClient;
@Component
public class ComprehensiveMcpCustomizers {
@Value("${mcp.timeout:30000}")
private int defaultTimeout;
@Value("${mcp.max-retries:3}")
private int maxRetries;
public McpSyncClientCustomizer syncCustomizer() {
return (name, spec) -> {
System.out.println("Customizing sync MCP client: " + name);
// Apply timeout settings based on client name
if (name.contains("slow-server")) {
// Longer timeout for slow servers
System.out.println(" - Using extended timeout for slow server");
} else {
// Standard timeout
System.out.println(" - Using standard timeout: " + defaultTimeout + "ms");
}
// Apply retry configuration
System.out.println(" - Max retries: " + maxRetries);
// Apply security settings
if (name.contains("production")) {
System.out.println(" - Applying production security settings");
}
};
}
public McpAsyncClientCustomizer asyncCustomizer() {
return (name, spec) -> {
System.out.println("Customizing async MCP client: " + name);
// Configure reactive execution
System.out.println(" - Configuring reactive execution model");
// Configure backpressure strategy
if (name.contains("high-throughput")) {
System.out.println(" - Using high-throughput backpressure strategy");
} else {
System.out.println(" - Using standard backpressure strategy");
}
// Apply timeout and retry for async operations
System.out.println(" - Async timeout: " + defaultTimeout + "ms");
System.out.println(" - Async retries: " + maxRetries);
};
}
}import java.util.Map;
public class ConditionalCustomizers {
private static final Map<String, McpSyncClientCustomizer> SYNC_CUSTOMIZERS = Map.of(
"weather", (name, spec) -> {
// Weather service specific customization
System.out.println("Weather service customization");
},
"database", (name, spec) -> {
// Database service specific customization
System.out.println("Database service customization");
},
"filesystem", (name, spec) -> {
// Filesystem service specific customization
System.out.println("Filesystem service customization");
}
);
public static McpSyncClientCustomizer forService(String serviceType) {
return SYNC_CUSTOMIZERS.getOrDefault(serviceType,
(name, spec) -> System.out.println("Default customization for: " + name));
}
public static McpAsyncClientCustomizer asyncForService(String serviceType) {
return (name, spec) -> {
switch (serviceType) {
case "high-latency":
System.out.println("High-latency async customization");
break;
case "low-latency":
System.out.println("Low-latency async customization");
break;
default:
System.out.println("Standard async customization");
}
};
}
}import java.util.List;
public class CompositeCustomizers {
public static McpSyncClientCustomizer composeSyncCustomizers(
List<McpSyncClientCustomizer> customizers) {
return (name, spec) -> {
for (McpSyncClientCustomizer customizer : customizers) {
customizer.customize(name, spec);
}
};
}
public static McpAsyncClientCustomizer composeAsyncCustomizers(
List<McpAsyncClientCustomizer> customizers) {
return (name, spec) -> {
for (McpAsyncClientCustomizer customizer : customizers) {
customizer.customize(name, spec);
}
};
}
}
// Usage
McpSyncClientCustomizer composite = CompositeCustomizers.composeSyncCustomizers(
List.of(
(name, spec) -> System.out.println("First customizer"),
(name, spec) -> System.out.println("Second customizer"),
(name, spec) -> System.out.println("Third customizer")
)
);@Component
public class EnvironmentBasedCustomizer {
private final String environment;
public EnvironmentBasedCustomizer(@Value("${spring.profiles.active:default}") String environment) {
this.environment = environment;
}
public McpSyncClientCustomizer getSyncCustomizer() {
return (name, spec) -> {
switch (environment) {
case "production":
applyProductionSettings(name, spec);
break;
case "staging":
applyStagingSettings(name, spec);
break;
case "development":
applyDevelopmentSettings(name, spec);
break;
default:
applyDefaultSettings(name, spec);
}
};
}
private void applyProductionSettings(String name, McpClient.SyncSpec spec) {
System.out.println("Production settings for: " + name);
// Strict timeouts, security, monitoring
}
private void applyStagingSettings(String name, McpClient.SyncSpec spec) {
System.out.println("Staging settings for: " + name);
// Similar to production but with debugging enabled
}
private void applyDevelopmentSettings(String name, McpClient.SyncSpec spec) {
System.out.println("Development settings for: " + name);
// Relaxed timeouts, verbose logging
}
private void applyDefaultSettings(String name, McpClient.SyncSpec spec) {
System.out.println("Default settings for: " + name);
}
}Note: These customizers are typically used during MCP client creation. The spring-ai-mcp library provides the interfaces but the actual client configuration is handled by the MCP SDK client builders (McpSyncClient, McpAsyncClient).
For typical usage with spring-ai-mcp tool callbacks and providers, customization happens during client creation rather than through the spring-ai-mcp library itself.