Spring Boot auto-configuration platform for Embabel Agent Framework, enabling annotation-driven profile activation and bootstrapping of agent configurations with MCP client support
/**
* Enhanced MCP client auto-configuration with error resilience.
* Extends Spring AI McpClientAutoConfiguration with graceful failure handling.
* Failed client initialization logged but doesn't block application startup.
*/
@AutoConfiguration(afterName = {
"org.springframework.ai.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration",
"org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration",
"org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration",
"org.springframework.ai.mcp.client.webflux.autoconfigure.SseWebFluxTransportAutoConfiguration",
"org.springframework.ai.mcp.client.webflux.autoconfigure.StreamableHttpWebFluxTransportAutoConfiguration"
})
@ConditionalOnClass(McpSchema.class)
@EnableConfigurationProperties(McpClientCommonProperties.class)
@ConditionalOnProperty(
prefix = "spring.ai.mcp.client",
name = "enabled",
havingValue = "true",
matchIfMissing = true
)
public class QuiteMcpClientAutoConfiguration extends McpClientAutoConfiguration {
/**
* Creates sync MCP clients with resilient initialization.
* Failed clients logged but excluded from bean list.
*
* @return List of successfully initialized sync clients (may be empty)
*/
@Bean
@ConditionalOnProperty(
prefix = "spring.ai.mcp.client",
name = "type",
havingValue = "SYNC",
matchIfMissing = true
)
List<McpSyncClient> mcpSyncClients(
McpSyncClientConfigurer mcpSyncClientConfigurer,
McpClientCommonProperties commonProperties,
ObjectProvider<List<NamedClientMcpTransport>> transportsProvider,
ObjectProvider<ClientMcpSyncHandlersRegistry> clientMcpSyncHandlersRegistry
);
/**
* Creates async MCP clients with resilient initialization.
* Failed clients logged but excluded from bean list.
*
* @return List of successfully initialized async clients (may be empty)
*/
@Bean
@ConditionalOnProperty(
prefix = "spring.ai.mcp.client",
name = "type",
havingValue = "ASYNC"
)
List<McpAsyncClient> mcpAsyncClients(
McpAsyncClientConfigurer mcpAsyncClientConfigurer,
McpClientCommonProperties commonProperties,
ObjectProvider<List<NamedClientMcpTransport>> transportsProvider,
ObjectProvider<ClientMcpAsyncHandlersRegistry> clientMcpAsyncHandlersRegistry
);
}Activation:
McpSchema.class on classpathspring.ai.mcp.client.enabled=true (default)spring.ai.mcp.client.type (SYNC or ASYNC)Error Handling:
// Pseudocode
if (properties.isInitialized()) {
try {
client.initialize(); // or .initialize().block() for async
clients.add(client);
} catch (Throwable t) {
logger.error("Failed to initialize MCP Client: {} - Application continues",
clientName, t);
// Client not added, app continues
}
}/**
* Synchronous MCP client interface.
*/
interface McpSyncClient {
void initialize();
// Other methods - see Spring AI docs
}
/**
* Asynchronous MCP client interface.
*/
interface McpAsyncClient {
Mono<Void> initialize();
// Other methods - see Spring AI docs
}
/**
* MCP protocol schema definitions.
*/
class McpSchema {
// Protocol schema
}
/**
* Named transport wrapper.
*/
record NamedClientMcpTransport(String name, McpTransport transport) {}
/**
* Transport interface.
*/
interface McpTransport {
// Transport methods
}/**
* MCP client configuration properties.
* Prefix: spring.ai.mcp.client
*/
class McpClientCommonProperties {
boolean enabled; // Default: true
String type; // SYNC or ASYNC, default: SYNC
String name; // Client name for identification
String version; // Client version for identification
Duration requestTimeout; // Request timeout
boolean initialized; // Auto-initialize on creation, default: true
}/**
* Sync handler registry for MCP protocol events.
*/
interface ClientMcpSyncHandlersRegistry {
void handleSampling(String transportName, SamplingRequest request);
void handleElicitation(String transportName, ElicitationRequest request);
void handleLogging(String transportName, LoggingMessageNotification notification);
void handleProgress(String transportName, ProgressNotification notification);
void handleToolListChanged(String transportName, List<Tool> newTools);
void handlePromptListChanged(String transportName, List<Prompt> newPrompts);
void handleResourceListChanged(String transportName, List<Resource> newResources);
Capabilities getCapabilities(String transportName);
}
/**
* Async handler registry for MCP protocol events.
*/
interface ClientMcpAsyncHandlersRegistry {
Mono<?> handleSampling(String transportName, SamplingRequest request);
Mono<?> handleElicitation(String transportName, ElicitationRequest request);
Mono<Void> handleLogging(String transportName, LoggingMessageNotification notification);
Mono<Void> handleProgress(String transportName, ProgressNotification notification);
Mono<Void> handleToolListChanged(String transportName, List<Tool> newTools);
Mono<Void> handlePromptListChanged(String transportName, List<Prompt> newPrompts);
Mono<Void> handleResourceListChanged(String transportName, List<Resource> newResources);
Mono<Capabilities> getCapabilities(String transportName);
}/**
* Customizer for sync MCP clients.
*/
interface McpSyncClientConfigurer {
McpClient.SyncSpec configure(String transportName, McpClient.SyncSpec spec);
}
/**
* Customizer for async MCP clients.
*/
interface McpAsyncClientConfigurer {
McpClient.AsyncSpec configure(String transportName, McpClient.AsyncSpec spec);
}Usage:
@Bean
public McpSyncClientConfigurer customConfigurer() {
return (transportName, spec) -> spec
.requestTimeout(Duration.ofSeconds(30))
// Additional customizations
;
}@Autowired
private List<McpSyncClient> mcpSyncClients;
public void useClient() {
if (!mcpSyncClients.isEmpty()) {
McpSyncClient client = mcpSyncClients.get(0);
// Use client (already initialized if spring.ai.mcp.client.initialized=true)
}
}@Autowired
private List<McpAsyncClient> mcpAsyncClients;
public Mono<Void> useClientAsync() {
if (!mcpAsyncClients.isEmpty()) {
McpAsyncClient client = mcpAsyncClients.get(0);
// Use reactive client
}
return Mono.empty();
}// When spring.ai.mcp.client.initialized=false
@Autowired
private List<McpSyncClient> clients;
public void initClients() {
for (McpSyncClient client : clients) {
try {
client.initialize();
} catch (Exception e) {
logger.error("Init failed", e);
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-embabel-agent--embabel-agent-platform-autoconfigure@0.3.0