CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-starter

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.

Overview
Eval results
Files

integration-mcp.mddocs/

MCP Integration

Model Context Protocol (MCP) client configuration with resilience patterns and STDIO connections for tool access.

Overview

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:

  • Resilient client initialization (failed clients don't block startup)
  • Sync and async clients
  • Automatic handler registration
  • Graceful degradation (partial success when some clients fail)
  • Spring AI integration
  • Custom configuration filter (excludes Spring AI's default MCP configuration)

Basic Configuration

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 creation

STDIO Connection Configuration

Single Connection

spring:
  ai:
    mcp:
      client:
        stdio:
          connections:
            docker-mcp:
              command: docker
              args:
                - run
                - -i
                - --rm
                - alpine/socat
                - STDIO
                - TCP:host.docker.internal:8811

Multiple Connections

spring:
  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.json

GitHub MCP Server

spring:
  ai:
    mcp:
      client:
        stdio:
          connections:
            github-mcp:
              command: npx
              args:
                - -y
                - @modelcontextprotocol/server-github

Browser Automation (Puppeteer)

spring:
  ai:
    mcp:
      client:
        stdio:
          connections:
            puppeteer-mcp:
              command: npx
              args:
                - -y
                - @modelcontextprotocol/server-puppeteer

Web Tools (Brave Search, Fetch, Wikipedia)

spring:
  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-wikipedia

Auto-Configuration Classes

QuiteMcpClientAutoConfiguration

Main 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:

  • Conditional on spring.ai.mcp.client.enabled (defaults to true)
  • Runs after transport auto-configurations
  • Creates sync MCP clients (List<McpSyncClient>) via mcpSyncClients() bean method
  • Creates async MCP clients (List<McpAsyncClient>) via mcpAsyncClients() bean method
  • Exception handling for resilient startup (failed clients logged, successful retained)
  • Registers handler callbacks automatically
  • Extends Spring AI's McpClientAutoConfiguration with enhanced resilience

AgentPlatformAutoConfigurationFilter

Filters 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.AgentPlatformAutoConfigurationFilter

Client Types

Synchronous Client

Default client type for blocking MCP operations.

spring:
  ai:
    mcp:
      client:
        type: SYNC

Use Case: Simple, blocking operations where you wait for MCP server responses.

Bean Type: List<McpSyncClient>

Asynchronous Client

Non-blocking client for reactive MCP operations.

spring:
  ai:
    mcp:
      client:
        type: ASYNC

Use Case: High-throughput scenarios where non-blocking I/O is preferred.

Bean Type: List<McpAsyncClient>

Handler Callbacks

The auto-configuration automatically registers handlers for MCP events:

Sampling Handler

Handles sampling requests from MCP servers.

Purpose: Process sampling requests for LLM-based operations initiated by the MCP server.

Elicitation Handler

Handles elicitation requests from MCP servers.

Purpose: Process requests for additional information or clarification from the agent.

Logging Handler

Handles log messages from MCP servers.

Purpose: Integrate MCP server log output into the application's logging system.

Progress Handler

Handles progress updates from MCP servers.

Purpose: Track long-running MCP operations and provide progress feedback.

Resource Change Handler

Handles notifications about resource changes from MCP servers.

Purpose: React to changes in MCP server resources (files, data, etc.).

Resilient Initialization

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:

  • Application starts even if some MCP servers are unavailable
  • Partial functionality maintained
  • Failed clients logged for debugging
  • Successful clients remain operational

Custom Client Configuration

Using McpSyncClientCustomizer

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;
        };
    }
}

Using McpAsyncClientCustomizer

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
        }
    }
}

Usage in Agents

Accessing MCP Clients (Java)

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);
        }
    }
}

Accessing MCP Clients (Kotlin)

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)
        }
    }
}

Environment Variables

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"

Timeout Configuration

MCP Client Timeout

spring:
  ai:
    mcp:
      client:
        request-timeout: 30s          # Request timeout duration (default: 30s)

Supports duration formats:

  • 30s - 30 seconds
  • 1m - 1 minute
  • 5m30s - 5 minutes 30 seconds
  • PT30S - ISO-8601 duration format

Resilience Patterns

Exponential Backoff

MCP 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:

  • Attempt 1: 5000ms (5 seconds)
  • Attempt 2: 25000ms (25 seconds)
  • Attempt 3: 125000ms (125 seconds)
  • Attempt 4+: 180000ms (180 seconds, capped)

Transient vs Permanent Failures

The retry mechanism distinguishes between transient and permanent failures:

Transient Failures (Retryable):

  • Network timeouts
  • HTTP 429 (Rate Limit)
  • HTTP 500, 502, 503, 504 (Server Errors)
  • Connection refused
  • Read timeout

Permanent Failures (Not Retryable):

  • HTTP 400 (Bad Request)
  • HTTP 401 (Unauthorized)
  • HTTP 403 (Forbidden)
  • Invalid API key
  • Malformed request

Conditional Tool Groups

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
)

Usage Example (Java)

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);
    }
}

Usage Example (Kotlin)

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)

Troubleshooting

Issue: MCP Client Not Initializing

Check:

  1. Verify spring.ai.mcp.client.enabled=true
  2. Ensure Spring Milestones repository is configured (Gradle users)
  3. Check that MCP server command is executable
  4. Review application logs for initialization errors

Issue: MCP Server Connection Fails

Solution: 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 refused

Issue: Conflicting MCP Configurations

Solution: 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 {
    // ...
}

Issue: STDIO Connection Not Working

Check:

  1. Command is in PATH or use absolute path
  2. Command has execute permissions
  3. Required arguments are correctly specified
  4. Server is compatible with MCP protocol

Issue: Timeout During Retry

Symptoms: Requests timeout before retry sequence completes

Solution: Adjust spring.ai.mcp.client.request-timeout to accommodate retry delays

Security Considerations

  1. Validate MCP server sources - Only connect to trusted MCP servers
  2. Secure credentials - Use environment variables for sensitive configuration
  3. Network isolation - Consider using Docker networking for MCP server isolation
  4. Timeout configuration - Set appropriate timeouts to prevent hanging operations
  5. Resource limits - Monitor MCP client resource usage

Best Practices

  1. Use sync clients for simple operations - Easier to reason about
  2. Use async clients for high throughput - Better performance for concurrent operations
  3. Configure timeouts - Prevent indefinite waiting
  4. Handle partial failures - Application logic should handle missing MCP clients
  5. Monitor MCP server health - Use Spring Boot Actuator for health checks
  6. Use connection names - Descriptive names for easier debugging
  7. Test MCP connections - Verify connectivity during development
  8. Document MCP dependencies - List required MCP servers for your application
tessl i tessl/maven-com-embabel-agent--embabel-agent-starter@0.3.1

docs

api-annotations.md

api-domain-model.md

api-invocation.md

api-tools.md

concepts-actions.md

concepts-agents.md

concepts-goals.md

concepts-invocation.md

concepts-tools.md

guides-creating-agents.md

guides-creating-tools.md

guides-defining-actions.md

guides-goal-achievement.md

guides-human-in-loop.md

guides-multimodal.md

index.md

integration-mcp.md

integration-model-providers.md

integration-spring-boot.md

LlmTool.md

quickstart.md

reference-component-scanning.md

reference-configuration-properties.md

reference-installation.md

reference-logging.md

reference-resilience.md

reference-streaming.md

tile.json