Base starter module for the Embabel Agent Framework providing core dependencies for building agentic flows on the JVM with Spring Boot integration and GOAP-based intelligent path finding.
Model Context Protocol (MCP) client configuration with resilience patterns and STDIO connections for tool access.
The embabel-agent-starter provides enhanced MCP (Model Context Protocol) client support through QuiteMcpClientAutoConfiguration. This integration allows agents to interact with MCP servers for accessing tools, resources, and prompts.
Key Features:
spring:
ai:
mcp:
client:
enabled: true # Enable MCP client (default: true)
type: SYNC # Client type: SYNC or ASYNC (default: SYNC)
name: embabel # Client implementation name
version: 1.0.0 # Client implementation version
request-timeout: 30s # Request timeout duration
initialized: true # Initialize client on creationspring:
ai:
mcp:
client:
stdio:
connections:
docker-mcp:
command: docker
args:
- run
- -i
- --rm
- alpine/socat
- STDIO
- TCP:host.docker.internal:8811spring:
ai:
mcp:
client:
stdio:
connections:
filesystem-server:
command: /usr/local/bin/mcp-filesystem-server
args:
- --root
- /data
docker-server:
command: docker
args:
- run
- -i
- --rm
- mcp/docker-server
custom-server:
command: node
args:
- /path/to/mcp-server.js
- --config
- /path/to/config.jsonspring:
ai:
mcp:
client:
stdio:
connections:
github-mcp:
command: npx
args:
- -y
- @modelcontextprotocol/server-githubspring:
ai:
mcp:
client:
stdio:
connections:
puppeteer-mcp:
command: npx
args:
- -y
- @modelcontextprotocol/server-puppeteerspring:
ai:
mcp:
client:
stdio:
connections:
brave-search-mcp:
command: npx
args:
- -y
- @modelcontextprotocol/server-brave-search
fetch-mcp:
command: npx
args:
- -y
- @modelcontextprotocol/server-fetch
wikipedia-mcp:
command: npx
args:
- -y
- @modelcontextprotocol/server-wikipediaMain auto-configuration class for MCP client setup.
package com.embabel.agent.autoconfigure.platform;
import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.client.McpSyncClient;
import org.springframework.ai.mcp.annotation.spring.ClientMcpAsyncHandlersRegistry;
import org.springframework.ai.mcp.annotation.spring.ClientMcpSyncHandlersRegistry;
import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
import org.springframework.ai.mcp.client.common.autoconfigure.configurer.McpAsyncClientConfigurer;
import org.springframework.ai.mcp.client.common.autoconfigure.configurer.McpSyncClientConfigurer;
import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
import org.springframework.beans.factory.ObjectProvider;
@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 a list of McpSyncClient instances based on available transports.
* Failed client initializations are logged but don't prevent application startup.
*/
@Bean
@ConditionalOnProperty(
prefix = "spring.ai.mcp.client",
name = "type",
havingValue = "SYNC",
matchIfMissing = true
)
public List<McpSyncClient> mcpSyncClients(
McpSyncClientConfigurer mcpSyncClientConfigurer,
McpClientCommonProperties commonProperties,
ObjectProvider<List<NamedClientMcpTransport>> transportsProvider,
ObjectProvider<ClientMcpSyncHandlersRegistry> clientMcpSyncHandlersRegistry
);
/**
* Creates a list of McpAsyncClient instances based on available transports.
* Failed client initializations are logged but don't prevent application startup.
*/
@Bean
@ConditionalOnProperty(
prefix = "spring.ai.mcp.client",
name = "type",
havingValue = "ASYNC"
)
public List<McpAsyncClient> mcpAsyncClients(
McpAsyncClientConfigurer mcpAsyncClientConfigurer,
McpClientCommonProperties commonProperties,
ObjectProvider<List<NamedClientMcpTransport>> transportsProvider,
ObjectProvider<ClientMcpAsyncHandlersRegistry> clientMcpAsyncHandlersRegistry
);
}Key Features:
spring.ai.mcp.client.enabled (defaults to true)List<McpSyncClient>) via mcpSyncClients() bean methodList<McpAsyncClient>) via mcpAsyncClients() bean methodMcpClientAutoConfiguration with enhanced resilienceFilters out Spring AI's default MCP configuration to prevent conflicts.
package com.embabel.agent.autoconfigure.platform;
public class AgentPlatformAutoConfigurationFilter
implements AutoConfigurationImportFilter {
@Override
public boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
// Excludes: org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration
}
}Purpose: Ensures Embabel's enhanced MCP client implementation is used instead of Spring AI's default, preventing startup failures from misconfigured MCP servers.
Registration: META-INF/spring.factories
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
com.embabel.agent.autoconfigure.platform.AgentPlatformAutoConfigurationFilterDefault client type for blocking MCP operations.
spring:
ai:
mcp:
client:
type: SYNCUse Case: Simple, blocking operations where you wait for MCP server responses.
Bean Type: List<McpSyncClient>
Non-blocking client for reactive MCP operations.
spring:
ai:
mcp:
client:
type: ASYNCUse Case: High-throughput scenarios where non-blocking I/O is preferred.
Bean Type: List<McpAsyncClient>
The auto-configuration automatically registers handlers for MCP events:
Handles sampling requests from MCP servers.
Purpose: Process sampling requests for LLM-based operations initiated by the MCP server.
Handles elicitation requests from MCP servers.
Purpose: Process requests for additional information or clarification from the agent.
Handles log messages from MCP servers.
Purpose: Integrate MCP server log output into the application's logging system.
Handles progress updates from MCP servers.
Purpose: Track long-running MCP operations and provide progress feedback.
Handles notifications about resource changes from MCP servers.
Purpose: React to changes in MCP server resources (files, data, etc.).
The QuiteMcpClientAutoConfiguration provides resilient client initialization:
// Pseudo-code representation
List<McpSyncClient> createMcpSyncClients(List<McpSyncClientCustomizer> customizers) {
List<McpSyncClient> successfulClients = new ArrayList<>();
for (McpSyncClientCustomizer customizer : customizers) {
try {
McpSyncClient client = createClient(customizer);
successfulClients.add(client);
} catch (Exception e) {
// Log error but continue with other clients
logger.warn("Failed to initialize MCP client", e);
}
}
return successfulClients;
}Benefits:
Customize sync MCP client creation:
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.ai.mcp.client.McpSyncClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class McpClientConfiguration {
@Bean
public McpSyncClientCustomizer myCustomizer() {
return client -> {
// Customize client configuration
client.setTimeout(Duration.ofSeconds(45));
client.setRetryPolicy(myRetryPolicy());
return client;
};
}
}Customize async MCP client creation:
import org.springframework.ai.mcp.client.McpAsyncClient
import org.springframework.ai.mcp.client.McpAsyncClientCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class McpClientConfiguration {
@Bean
fun myAsyncCustomizer(): McpAsyncClientCustomizer {
return McpAsyncClientCustomizer { client ->
// Customize async client
client.setTimeout(Duration.ofSeconds(45))
client
}
}
}import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.stereotype.Component;
@Component
public class MyAgent {
private final List<McpSyncClient> mcpClients;
public MyAgent(List<McpSyncClient> mcpClients) {
this.mcpClients = mcpClients;
}
@Action
public Result performAction(Input input) {
for (McpSyncClient client : mcpClients) {
// Use MCP client to access tools/resources
String result = client.callTool("tool-name", params);
}
}
}import org.springframework.ai.mcp.client.McpSyncClient
import org.springframework.stereotype.Component
@Component
class MyAgent(
private val mcpClients: List<McpSyncClient>
) {
@Action
fun performAction(input: Input): Result {
mcpClients.forEach { client ->
// Use MCP client
val result = client.callTool("tool-name", params)
}
}
}MCP client configuration can reference environment variables:
spring:
ai:
mcp:
client:
stdio:
connections:
secure-server:
command: ${MCP_SERVER_COMMAND}
args:
- ${MCP_SERVER_ARGS}export MCP_SERVER_COMMAND=/usr/local/bin/mcp-server
export MCP_SERVER_ARGS="--token=secret"spring:
ai:
mcp:
client:
request-timeout: 30s # Request timeout duration (default: 30s)Supports duration formats:
30s - 30 seconds1m - 1 minute5m30s - 5 minutes 30 secondsPT30S - ISO-8601 duration formatMCP operations use exponential backoff for retries:
embabel:
agent:
platform:
models:
anthropic:
max-attempts: 10 # Maximum retry attempts
backoff-millis: 5000 # Initial backoff in milliseconds
backoff-multiplier: 5.0 # Backoff multiplier
backoff-max-interval: 180000 # Maximum backoff interval (3 minutes)Backoff Calculation:
Delay = min(backoff-millis * (backoff-multiplier ^ attempt), backoff-max-interval)Example Retry Sequence:
The retry mechanism distinguishes between transient and permanent failures:
Transient Failures (Retryable):
Permanent Failures (Not Retryable):
Use @ConditionalOnMcpConnection to create tool groups based on configured MCP connections.
package com.embabel.agent.spi.config.spring
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Conditional(OnMcpConnectionCondition::class)
annotation class ConditionalOnMcpConnection(
vararg val value: String
)import com.embabel.agent.spi.config.spring.ConditionalOnMcpConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyToolsConfiguration {
@Bean
@ConditionalOnMcpConnection("github-mcp")
public ToolGroup githubTools() {
// Only created when github-mcp connection is configured
return ToolGroup.create("github", githubToolList);
}
@Bean
@ConditionalOnMcpConnection("brave-search-mcp", "fetch-mcp")
public ToolGroup webTools() {
// Created when ANY of the specified connections exists
return ToolGroup.create("web", webToolList);
}
}import com.embabel.agent.spi.config.spring.ConditionalOnMcpConnection
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class MyToolsConfiguration {
@Bean
@ConditionalOnMcpConnection("github-mcp", "docker-mcp")
fun githubToolsGroup(): ToolGroup {
// Created if either github-mcp or docker-mcp is configured
return McpToolGroup(
name = "github-tools",
description = "GitHub API tools",
clients = mcpSyncClients
)
}
}Built-in Tool Groups Using This Annotation:
mathToolGroup() - Math operations (always created)mcpWebToolsGroup() - Web tools (requires brave-search-mcp, fetch-mcp, wikipedia-mcp, or docker-mcp)mapsToolsGroup() - Maps tools (requires google-maps-mcp or docker-mcp)browserAutomationWebToolsGroup() - Browser automation (requires puppeteer-mcp or docker-mcp)githubToolsGroup() - GitHub tools (requires github-mcp or docker-mcp)Check:
spring.ai.mcp.client.enabled=trueSolution: The resilient configuration allows the application to start even if MCP servers fail. Check logs for specific errors:
WARN: Failed to initialize MCP client for connection 'docker-mcp': Connection refusedSolution: The AgentPlatformAutoConfigurationFilter should automatically exclude Spring AI's default configuration. If conflicts persist:
@SpringBootApplication(exclude = {
org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration.class
})
public class MyApplication {
// ...
}Check:
Symptoms: Requests timeout before retry sequence completes
Solution: Adjust spring.ai.mcp.client.request-timeout to accommodate retry delays
tessl i tessl/maven-com-embabel-agent--embabel-agent-starter@0.3.1docs