CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-mcpserver

Discover and Export available Agent(s) as MCP Servers

Overview
Eval results
Files

server-configuration.mddocs/api/

Server Configuration API

API reference for MCP server auto-configuration classes, configuration beans, and conditional annotations.

Package

com.embabel.agent.mcpserver.config        // Configuration classes
com.embabel.agent.mcpserver.sync.config   // Sync config
com.embabel.agent.mcpserver.async.config  // Async config

Overview

The library uses Spring Boot auto-configuration to automatically set up the MCP server based on the spring.ai.mcp.server.type property. All configuration is annotation-driven with sensible defaults.

Configuration Classes

AbstractMcpServerConfiguration { .api }

Base configuration class providing common functionality.

package com.embabel.agent.mcpserver.config

abstract class AbstractMcpServerConfiguration {
    protected abstract fun createServerInfo(
        applicationName: String,
        executionMode: McpExecutionMode
    ): ServerInfo

    protected abstract fun createToolRegistry(): ToolRegistry

    protected fun getApplicationName(environment: Environment): String {
        return environment.getProperty("spring.application.name", "agent-api")
    }
}

Common Behavior:

  • Provides server info creation
  • Manages tool registry instantiation
  • Resolves application name from properties

McpSyncServerConfiguration { .api }

Auto-configuration for synchronous mode.

package com.embabel.agent.mcpserver.sync.config

@Configuration
@ConditionalOnProperty(
    value = ["spring.ai.mcp.server.type"],
    havingValue = "SYNC",
    matchIfMissing = true
)
class McpSyncServerConfiguration : AbstractMcpServerConfiguration() {

    @Bean
    fun syncServerInfo(environment: Environment): ServerInfo {
        return createServerInfo(
            applicationName = getApplicationName(environment),
            executionMode = McpExecutionMode.SYNC
        )
    }

    @Bean
    fun syncToolRegistry(): ToolRegistry {
        return createToolRegistry()
    }

    @Bean
    fun mcpSyncServer(
        toolRegistry: ToolRegistry,
        resourcePublishers: List<McpResourcePublisher>,
        promptPublishers: List<McpPromptPublisher>
    ): McpSyncServer {
        // Create and configure sync server
    }

    @Bean
    fun syncServerStrategy(
        mcpSyncServer: McpSyncServer,
        toolRegistry: ToolRegistry,
        serverInfo: ServerInfo
    ): McpServerStrategy {
        return SyncServerStrategy(mcpSyncServer, toolRegistry, serverInfo)
    }
}

Activated When:

  • Property spring.ai.mcp.server.type=SYNC, or
  • Property not set (default)

Provides Beans:

  • ServerInfo - Server metadata
  • ToolRegistry - Tool registry implementation
  • McpSyncServer - Synchronous MCP server
  • McpServerStrategy - Sync server strategy

Usage:

# application.properties
spring.ai.mcp.server.type=SYNC
spring.application.name=my-service

McpAsyncServerConfiguration { .api }

Auto-configuration for asynchronous mode.

package com.embabel.agent.mcpserver.async.config

@Configuration
@ConditionalOnProperty(
    value = ["spring.ai.mcp.server.type"],
    havingValue = "ASYNC"
)
class McpAsyncServerConfiguration : AbstractMcpServerConfiguration() {

    @Bean
    fun asyncServerInfo(environment: Environment): ServerInfo {
        return createServerInfo(
            applicationName = getApplicationName(environment),
            executionMode = McpExecutionMode.ASYNC
        )
    }

    @Bean
    fun asyncToolRegistry(): ToolRegistry {
        return createToolRegistry()
    }

    @Bean
    fun mcpAsyncServer(
        toolRegistry: ToolRegistry,
        resourcePublishers: List<McpAsyncResourcePublisher>,
        promptPublishers: List<McpAsyncPromptPublisher>
    ): McpAsyncServer {
        // Create and configure async server
    }

    @Bean
    fun asyncServerStrategy(
        mcpAsyncServer: McpAsyncServer,
        toolRegistry: ToolRegistry,
        serverInfo: ServerInfo
    ): McpServerStrategy {
        return AsyncServerStrategy(mcpAsyncServer, toolRegistry, serverInfo)
    }
}

Activated When:

  • Property spring.ai.mcp.server.type=ASYNC

Provides Beans:

  • ServerInfo - Server metadata
  • ToolRegistry - Tool registry implementation
  • McpAsyncServer - Asynchronous MCP server
  • McpServerStrategy - Async server strategy

Usage:

# application.properties
spring.ai.mcp.server.type=ASYNC
spring.application.name=my-service

Configuration Properties

Standard Properties

# Execution mode (SYNC or ASYNC)
spring.ai.mcp.server.type=SYNC

# Application name
spring.application.name=my-agent-api

Custom Configuration Bean { .api }

Create type-safe configuration properties:

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

@Configuration
@ConfigurationProperties(prefix = "mcp.server")
data class McpServerConfig(
    var maxTools: Int = 100,
    var maxResources: Int = 50,
    var enableMetrics: Boolean = true,
    var timeoutSeconds: Long = 30,
    var features: FeatureFlags = FeatureFlags()
)

data class FeatureFlags(
    var experimental: Boolean = false,
    var analytics: Boolean = true,
    var debugTools: Boolean = false
)

Properties File:

# application.properties
mcp.server.max-tools=200
mcp.server.max-resources=100
mcp.server.enable-metrics=true
mcp.server.timeout-seconds=60

mcp.server.features.experimental=false
mcp.server.features.analytics=true
mcp.server.features.debug-tools=false

Usage:

@Service
class ConfigurableService(
    private val config: McpServerConfig
) {
    fun doSomething() {
        println("Max tools: ${config.maxTools}")
        println("Experimental enabled: ${config.features.experimental}")
    }
}

Validated Configuration { .api }

Add validation constraints:

import javax.validation.constraints.Min
import javax.validation.constraints.Max
import javax.validation.constraints.NotBlank
import org.springframework.validation.annotation.Validated

@Configuration
@ConfigurationProperties(prefix = "mcp.server")
@Validated
data class ValidatedConfig(
    @field:Min(1)
    @field:Max(1000)
    var maxTools: Int = 100,

    @field:Min(1)
    @field:Max(500)
    var maxResources: Int = 50,

    @field:Min(1)
    var timeoutSeconds: Long = 30,

    @field:NotBlank
    var serverName: String = "mcp-server"
)

Validation Errors:

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target org.springframework.boot.context.properties.bind.BindException:
Property: mcp.server.maxTools
Value: 2000
Reason: must be less than or equal to 1000

Conditional Annotations

@ConditionalOnProperty

Activate beans based on property values:

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty

// Activated when property matches value
@Service
@ConditionalOnProperty(
    value = ["spring.ai.mcp.server.type"],
    havingValue = "SYNC",
    matchIfMissing = true  // Active if property not set
)
class SyncOnlyService { /* ... */ }

// Activated when property exists (any value)
@Service
@ConditionalOnProperty(name = ["features.enabled"])
class FeatureService { /* ... */ }

// Multiple conditions (AND logic)
@Service
@ConditionalOnProperty(
    value = ["spring.ai.mcp.server.type"],
    havingValue = "ASYNC"
)
@ConditionalOnProperty(
    name = ["features.advanced"],
    havingValue = "true"
)
class AdvancedAsyncService { /* ... */ }

@Profile

Activate beans based on active profile:

import org.springframework.context.annotation.Profile

// Development profile only
@Service
@Profile("development")
class DevService { /* ... */ }

// Production profile only
@Service
@Profile("production")
class ProdService { /* ... */ }

// Multiple profiles (OR logic)
@Service
@Profile("development", "staging")
class DevOrStagingService { /* ... */ }

// Not production (negation)
@Service
@Profile("!production")
class NonProdService { /* ... */ }

@ConditionalOnBean

Activate when specific beans exist:

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean

// Activated if ToolRegistry bean exists
@Service
@ConditionalOnBean(ToolRegistry::class)
class ToolDependentService { /* ... */ }

// Activated if no custom implementation exists
@Service
@ConditionalOnMissingBean(CustomRegistry::class)
class DefaultRegistryService { /* ... */ }

@ConditionalOnClass

Activate when classes are present on classpath:

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass

// Activated if WebFlux is available
@Configuration
@ConditionalOnClass(name = ["org.springframework.web.reactive.config.WebFluxConfigurer"])
class WebFluxConfiguration { /* ... */ }

Environment-Specific Configuration

Profile-Based Files

application.properties                 # Common configuration
application-dev.properties            # Development overrides
application-staging.properties        # Staging overrides
application-prod.properties           # Production overrides

application-dev.properties:

spring.ai.mcp.server.type=SYNC
spring.application.name=myapp-dev

features.experimental=true
features.debug-tools=true

logging.level.com.embabel.agent.mcpserver=DEBUG

application-prod.properties:

spring.ai.mcp.server.type=ASYNC
spring.application.name=myapp-prod

features.experimental=false
features.debug-tools=false

logging.level.com.embabel.agent.mcpserver=INFO

Activation:

# Command line
java -jar app.jar --spring.profiles.active=prod

# Environment variable
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar

Environment Variables

Override properties with environment variables:

# Property: spring.ai.mcp.server.type
# Env var: SPRING_AI_MCP_SERVER_TYPE
export SPRING_AI_MCP_SERVER_TYPE=ASYNC

# Property: mcp.server.max-tools
# Env var: MCP_SERVER_MAX_TOOLS
export MCP_SERVER_MAX_TOOLS=500

java -jar app.jar

Access in Code:

@Service
class EnvAwareService(
    @Value("\${API_KEY:default-key}")
    private val apiKey: String,

    @Value("\${MAX_CONNECTIONS:10}")
    private val maxConnections: Int
) {
    // Use apiKey and maxConnections
}

Custom Configuration Classes

Module Configuration { .api }

@Configuration
@ConfigurationProperties(prefix = "mcp.auth")
data class AuthConfig(
    var enabled: Boolean = false,
    var provider: String = "basic",
    var tokenExpiry: Long = 3600
)

@Configuration
@ConfigurationProperties(prefix = "mcp.storage")
data class StorageConfig(
    var type: String = "memory",
    var maxSize: Long = 1000,
    var persistEnabled: Boolean = false
)

@Service
class AuthService(
    private val authConfig: AuthConfig
) {
    fun authenticate(token: String): Boolean {
        if (!authConfig.enabled) return true
        // Use authConfig.provider, authConfig.tokenExpiry
        return false
    }
}

Properties:

mcp.auth.enabled=true
mcp.auth.provider=oauth2
mcp.auth.token-expiry=7200

mcp.storage.type=redis
mcp.storage.max-size=10000
mcp.storage.persist-enabled=true

Dynamic Configuration { .api }

import org.springframework.cloud.context.config.annotation.RefreshScope

@Service
@RefreshScope  // Enables runtime refresh
class DynamicConfigService(
    private val config: McpServerConfig
) {
    fun getCurrentMaxTools(): Int {
        return config.maxTools  // Reflects latest value after refresh
    }
}

Refresh Configuration:

# Trigger refresh (requires Spring Cloud)
curl -X POST http://localhost:8080/actuator/refresh

Testing Configuration

Test Configuration Class { .api }

import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Primary

@TestConfiguration
class TestMcpConfiguration {

    @Bean
    @Primary
    fun testServerConfig(): McpServerConfig {
        return McpServerConfig(
            maxTools = 10,
            maxResources = 5,
            enableMetrics = false,
            timeoutSeconds = 5
        )
    }

    @Bean
    fun testToolRegistry(): ToolRegistry {
        return InMemoryToolRegistry()
    }
}

Test Properties { .api }

# src/test/resources/application-test.properties
spring.ai.mcp.server.type=SYNC
spring.application.name=test-app

features.experimental=false
mcp.server.timeout-seconds=5
mcp.storage.type=memory

Integration Test { .api }

import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.TestPropertySource

@SpringBootTest
@TestPropertySource(properties = [
    "spring.ai.mcp.server.type=SYNC",
    "features.experimental=true",
    "mcp.server.max-tools=50"
])
class IntegrationTest {

    @Autowired
    private lateinit var serverStrategy: McpServerStrategy

    @Autowired
    private lateinit var config: McpServerConfig

    @Test
    fun `should configure server correctly`() {
        assertEquals(McpExecutionMode.SYNC, serverStrategy.getExecutionMode())
        assertEquals(50, config.maxTools)
    }
}

Best Practices

  1. Use Type-Safe Configuration: Prefer @ConfigurationProperties over @Value

    // Good
    @ConfigurationProperties(prefix = "mcp.server")
    data class McpConfig(var maxTools: Int = 100)
    
    // Avoid scattered @Value
    @Value("\${mcp.server.max-tools:100}")
  2. Provide Defaults: Always specify sensible default values

    data class Config(
        var maxTools: Int = 100,        // Reasonable default
        var timeoutSeconds: Long = 30   // Safe default
    )
  3. Validate Configuration: Use validation annotations

    @field:Min(1)
    @field:Max(1000)
    var maxTools: Int = 100
  4. Document Properties: Add KDoc or comments

    /**
     * Maximum number of tools (1-1000).
     * Default: 100
     */
    var maxTools: Int = 100
  5. Use Profiles Wisely: Keep environment-specific settings in profile files

    # Common in application.properties
    spring.application.name=myapp
    
    # Environment-specific in application-{profile}.properties
    spring.ai.mcp.server.type=ASYNC  # in application-prod.properties

See Also

  • Configuration Guide - Complete configuration examples
  • Execution Modes Guide - Mode selection
  • Getting Started Guide - Initial setup
  • Integration Patterns Guide - Full examples
tessl i tessl/maven-com-embabel-agent--embabel-agent-mcpserver@0.3.1

docs

index.md

tile.json