Discover and Export available Agent(s) as MCP Servers
API reference for core domain models including execution modes, server information, tool specifications, capabilities, and health status.
com.embabel.agent.mcpserver.domainDomain types represent core concepts in the MCP server architecture. These immutable data classes provide type-safe access to server metadata, configuration, and runtime state.
Enum representing MCP server execution modes.
package com.embabel.agent.mcpserver.domain
enum class McpExecutionMode {
SYNC,
ASYNC
}Values:
SYNC - Synchronous blocking modeASYNC - Asynchronous non-blocking modeUsage:
import com.embabel.agent.mcpserver.domain.McpExecutionMode
// Check mode
val mode = serverStrategy.getExecutionMode()
when (mode) {
McpExecutionMode.SYNC -> {
println("Running in synchronous mode")
}
McpExecutionMode.ASYNC -> {
println("Running in asynchronous mode")
}
}
// Mode-specific logic
fun performOperation() {
val mode = serverStrategy.getExecutionMode()
if (mode == McpExecutionMode.ASYNC) {
performAsyncOperation()
} else {
performSyncOperation()
}
}
// String representation
val modeString = mode.toString() // "SYNC" or "ASYNC"
val modeValue = mode.name // "SYNC" or "ASYNC"Comparison:
// Check for specific mode
val isSyncMode = mode == McpExecutionMode.SYNC
val isAsyncMode = mode == McpExecutionMode.ASYNC
// Pattern matching
val description = when (mode) {
McpExecutionMode.SYNC -> "Blocking operations, simpler code"
McpExecutionMode.ASYNC -> "Non-blocking operations, better scalability"
}Immutable data class containing server metadata.
package com.embabel.agent.mcpserver.domain
data class ServerInfo(
val name: String,
val version: String,
val executionMode: McpExecutionMode,
val capabilities: Set<McpCapability>,
val metadata: Map<String, String> = emptyMap()
)Properties:
name: String - Application name (from spring.application.name)version: String - Library version (e.g., "0.3.3")executionMode: McpExecutionMode - Current execution modecapabilities: Set<McpCapability> - Supported MCP capabilitiesmetadata: Map<String, String> - Additional custom metadataUsage:
import com.embabel.agent.mcpserver.domain.ServerInfo
@Service
class ServerInfoService(
private val serverStrategy: McpServerStrategy
) {
fun displayServerInfo() {
serverStrategy.getServerInfo()
.subscribe { info ->
println("Server Name: ${info.name}")
println("Version: ${info.version}")
println("Mode: ${info.executionMode}")
println("Capabilities: ${info.capabilities}")
println("Metadata: ${info.metadata}")
}
}
fun checkCapability(capability: McpCapability): Mono<Boolean> {
return serverStrategy.getServerInfo()
.map { info -> info.capabilities.contains(capability) }
}
fun getMetadataValue(key: String): Mono<String?> {
return serverStrategy.getServerInfo()
.map { info -> info.metadata[key] }
}
fun formatServerSummary(): Mono<String> {
return serverStrategy.getServerInfo()
.map { info ->
"""
${info.name} v${info.version}
Mode: ${info.executionMode}
Capabilities: ${info.capabilities.size}
""".trimIndent()
}
}
}REST API:
@RestController
@RequestMapping("/api/server")
class ServerController(
private val serverStrategy: McpServerStrategy
) {
@GetMapping("/info")
fun getServerInfo(): Mono<ResponseEntity<ServerInfo>> {
return serverStrategy.getServerInfo()
.map { ResponseEntity.ok(it) }
}
@GetMapping("/info/summary")
fun getServerSummary(): Mono<ResponseEntity<ServerSummary>> {
return serverStrategy.getServerInfo()
.map { info ->
ServerSummary(
name = info.name,
version = info.version,
mode = info.executionMode.toString()
)
}
.map { ResponseEntity.ok(it) }
}
}
data class ServerSummary(
val name: String,
val version: String,
val mode: String
)Immutable data class describing a tool's metadata.
package com.embabel.agent.mcpserver.domain
data class ToolSpecification(
val name: String,
val description: String,
val inputSchema: String?,
val outputSchema: String?
)Properties:
name: String - Tool name (unique identifier)description: String - Human-readable descriptioninputSchema: String? - JSON schema for input (optional)outputSchema: String? - JSON schema for output (optional)Usage:
import com.embabel.agent.mcpserver.domain.ToolSpecification
// Create specification
val spec = ToolSpecification(
name = "calculateSum",
description = "Calculate sum of two numbers",
inputSchema = """
{
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
""".trimIndent(),
outputSchema = """
{
"type": "object",
"properties": {
"result": {"type": "number"}
}
}
""".trimIndent()
)
// Query tools
@Service
class ToolSpecificationService(
private val toolRegistry: ToolRegistry
) {
fun getToolSpecification(toolName: String): Mono<ToolSpecification> {
return toolRegistry.findToolCallback(toolName)
.map { callback ->
ToolSpecification(
name = callback.name,
description = callback.description,
inputSchema = callback.inputTypeSchema,
outputSchema = null
)
}
}
fun getAllToolSpecifications(): Mono<List<ToolSpecification>> {
return toolRegistry.listToolCallbacks()
.map { callbacks ->
callbacks.map { callback ->
ToolSpecification(
name = callback.name,
description = callback.description,
inputSchema = callback.inputTypeSchema,
outputSchema = null
)
}
}
}
fun hasInputSchema(toolName: String): Mono<Boolean> {
return getToolSpecification(toolName)
.map { it.inputSchema != null }
.defaultIfEmpty(false)
}
}Enum representing MCP protocol capabilities.
package com.embabel.agent.mcpserver.domain
enum class McpCapability {
TOOLS,
RESOURCES,
PROMPTS,
SAMPLING
}Values:
TOOLS - Tool execution supportRESOURCES - Resource exposure supportPROMPTS - Prompt definition supportSAMPLING - Sampling/completion supportUsage:
import com.embabel.agent.mcpserver.domain.McpCapability
@Service
class CapabilityService(
private val serverStrategy: McpServerStrategy
) {
fun hasCapability(capability: McpCapability): Mono<Boolean> {
return serverStrategy.getServerInfo()
.map { info -> info.capabilities.contains(capability) }
}
fun checkAllCapabilities(required: Set<McpCapability>): Mono<Boolean> {
return serverStrategy.getServerInfo()
.map { info -> info.capabilities.containsAll(required) }
}
fun listCapabilities(): Mono<Set<McpCapability>> {
return serverStrategy.getServerInfo()
.map { it.capabilities }
}
fun describeCapabilities(): Mono<Map<McpCapability, String>> {
return serverStrategy.getServerInfo()
.map { info ->
info.capabilities.associateWith { capability ->
when (capability) {
McpCapability.TOOLS -> "Execute tools"
McpCapability.RESOURCES -> "Access resources"
McpCapability.PROMPTS -> "Use prompts"
McpCapability.SAMPLING -> "Generate completions"
}
}
}
}
}
// Check required capabilities
@Service
class FeatureGuard(
private val serverStrategy: McpServerStrategy
) {
fun requireCapabilities(vararg capabilities: McpCapability): Mono<Void> {
return serverStrategy.getServerInfo()
.flatMap { info ->
val hasAll = capabilities.all { it in info.capabilities }
if (hasAll) {
Mono.empty()
} else {
val missing = capabilities.filter { it !in info.capabilities }
Mono.error(
MissingCapabilitiesException(
"Missing capabilities: ${missing.joinToString()}"
)
)
}
}
}
}
class MissingCapabilitiesException(message: String) : RuntimeException(message)Immutable data class representing server health.
package com.embabel.agent.mcpserver.domain
data class ServerHealthStatus(
val isHealthy: Boolean,
val message: String,
val timestamp: Long = System.currentTimeMillis(),
val details: Map<String, Any> = emptyMap()
)Properties:
isHealthy: Boolean - Overall health statusmessage: String - Status messagetimestamp: Long - Status check timestamp (milliseconds)details: Map<String, Any> - Additional health detailsUsage:
import com.embabel.agent.mcpserver.domain.ServerHealthStatus
@Service
class HealthCheckService(
private val serverStrategy: McpServerStrategy
) {
fun checkHealth(): Mono<ServerHealthStatus> {
return serverStrategy.checkHealth()
.doOnNext { status ->
if (status.isHealthy) {
logger.info("Server healthy: ${status.message}")
} else {
logger.warn("Server unhealthy: ${status.message}")
}
}
}
fun waitUntilHealthy(timeout: Duration): Mono<Boolean> {
return serverStrategy.checkHealth()
.map { it.isHealthy }
.repeatWhenEmpty { it.delayElements(Duration.ofSeconds(1)) }
.timeout(timeout)
.onErrorReturn(false)
}
fun getHealthDetails(): Mono<Map<String, Any>> {
return serverStrategy.checkHealth()
.map { it.details }
}
fun formatHealthReport(): Mono<String> {
return serverStrategy.checkHealth()
.map { status ->
buildString {
appendLine("Health Status: ${if (status.isHealthy) "Healthy" else "Unhealthy"}")
appendLine("Message: ${status.message}")
appendLine("Timestamp: ${status.timestamp}")
if (status.details.isNotEmpty()) {
appendLine("Details:")
status.details.forEach { (key, value) ->
appendLine(" $key: $value")
}
}
}
}
}
companion object {
private val logger = LoggerFactory.getLogger(HealthCheckService::class.java)
}
}REST API:
@RestController
@RequestMapping("/api/health")
class HealthController(
private val serverStrategy: McpServerStrategy
) {
@GetMapping
fun checkHealth(): Mono<ResponseEntity<ServerHealthStatus>> {
return serverStrategy.checkHealth()
.map { status ->
val httpStatus = if (status.isHealthy) {
HttpStatus.OK
} else {
HttpStatus.SERVICE_UNAVAILABLE
}
ResponseEntity.status(httpStatus).body(status)
}
}
@GetMapping("/simple")
fun simpleHealthCheck(): Mono<ResponseEntity<HealthResponse>> {
return serverStrategy.checkHealth()
.map { status ->
HealthResponse(
healthy = status.isHealthy,
timestamp = status.timestamp
)
}
.map { ResponseEntity.ok(it) }
}
@GetMapping("/wait")
fun waitForHealthy(
@RequestParam(defaultValue = "30") timeoutSeconds: Long
): Mono<ResponseEntity<String>> {
val timeout = Duration.ofSeconds(timeoutSeconds)
return serverStrategy.checkHealth()
.map { it.isHealthy }
.repeatWhenEmpty { it.delayElements(Duration.ofSeconds(1)) }
.timeout(timeout)
.map { healthy ->
if (healthy) {
ResponseEntity.ok("Server is healthy")
} else {
ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body("Server not healthy")
}
}
.onErrorResume(TimeoutException::class.java) { _ ->
Mono.just(
ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
.body("Health check timeout")
)
}
}
}
data class HealthResponse(
val healthy: Boolean,
val timestamp: Long
)Custom Health Details:
// Creating custom health status
val healthStatus = ServerHealthStatus(
isHealthy = true,
message = "All systems operational",
details = mapOf(
"toolsRegistered" to 42,
"resourcesAvailable" to 15,
"uptime" to System.currentTimeMillis(),
"memoryUsage" to "512MB / 1024MB"
)
)
// Unhealthy status
val unhealthyStatus = ServerHealthStatus(
isHealthy = false,
message = "Database connection failed",
details = mapOf(
"error" to "Connection timeout",
"retryAttempts" to 3,
"lastAttempt" to System.currentTimeMillis()
)
)@Service
class ModeAwareService(
private val serverStrategy: McpServerStrategy
) {
fun performModeSpecificOperation(): Mono<String> {
val mode = serverStrategy.getExecutionMode()
return when (mode) {
McpExecutionMode.SYNC -> {
Mono.just("Performing synchronous operation")
}
McpExecutionMode.ASYNC -> {
Mono.just("Performing asynchronous operation")
}
}
}
fun isAsyncMode(): Boolean {
return serverStrategy.getExecutionMode() == McpExecutionMode.ASYNC
}
fun isSyncMode(): Boolean {
return serverStrategy.getExecutionMode() == McpExecutionMode.SYNC
}
}@Service
class MetadataService(
private val serverStrategy: McpServerStrategy
) {
fun getServerName(): Mono<String> {
return serverStrategy.getServerInfo()
.map { it.name }
}
fun getServerVersion(): Mono<String> {
return serverStrategy.getServerInfo()
.map { it.version }
}
fun getFullServerDescription(): Mono<String> {
return serverStrategy.getServerInfo()
.map { info ->
"${info.name} v${info.version} (${info.executionMode})"
}
}
fun getAllMetadata(): Mono<Map<String, Any>> {
return serverStrategy.getServerInfo()
.map { info ->
mapOf(
"name" to info.name,
"version" to info.version,
"mode" to info.executionMode.toString(),
"capabilities" to info.capabilities.map { it.toString() },
"metadata" to info.metadata
)
}
}
}@Service
class HealthMonitor(
private val serverStrategy: McpServerStrategy
) {
@Scheduled(fixedRate = 60000) // Every minute
fun monitorHealth() {
serverStrategy.checkHealth()
.subscribe { status ->
if (!status.isHealthy) {
logger.error("Health check failed: ${status.message}")
alertAdministrators(status)
} else {
logger.debug("Health check passed: ${status.message}")
}
}
}
fun collectHealthHistory(): Flux<ServerHealthStatus> {
return Flux.interval(Duration.ofSeconds(10))
.flatMap { serverStrategy.checkHealth() }
.take(Duration.ofMinutes(5))
}
private fun alertAdministrators(status: ServerHealthStatus) {
// Send alerts
}
companion object {
private val logger = LoggerFactory.getLogger(HealthMonitor::class.java)
}
}Use Type-Safe Access: Leverage domain types for type safety
val mode: McpExecutionMode = serverStrategy.getExecutionMode()Pattern Matching: Use when expressions for mode handling
when (mode) {
McpExecutionMode.SYNC -> handleSync()
McpExecutionMode.ASYNC -> handleAsync()
}Capability Checking: Verify capabilities before operations
serverInfo.capabilities.contains(McpCapability.TOOLS)Health Monitoring: Regularly check health status
@Scheduled(fixedRate = 60000)
fun checkHealth() { /* ... */ }Immutability: Domain types are immutable; use copy for modifications
val updated = serverInfo.copy(metadata = newMetadata)