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

factories.mddocs/api/

Factories API

API reference for resource and prompt specification factories that simplify creation of MCP resources and prompts.

Package

com.embabel.agent.mcpserver.sync   // Sync mode factories
com.embabel.agent.mcpserver.async  // Async mode factories

Overview

Factories provide convenient methods for creating resource and prompt specifications with type-safe APIs. They handle boilerplate code and ensure correct MCP protocol structures.

Resource Factories (Sync Mode)

SyncResourceSpecificationFactory { .api }

Factory for creating synchronous resource specifications.

package com.embabel.agent.mcpserver.sync

import io.modelcontextprotocol.server.McpServerFeatures
import io.modelcontextprotocol.server.McpSyncServerExchange

object SyncResourceSpecificationFactory {

    fun staticSyncResourceSpecification(
        uri: String,
        name: String,
        description: String,
        content: String,
        mimeType: String
    ): McpServerFeatures.SyncResourceSpecification

    fun syncResourceSpecification(
        uri: String,
        name: String,
        description: String,
        resourceLoader: (McpSyncServerExchange) -> String,
        mimeType: String
    ): McpServerFeatures.SyncResourceSpecification
}

staticSyncResourceSpecification { .api }

Create a static resource with fixed content.

fun staticSyncResourceSpecification(
    uri: String,
    name: String,
    description: String,
    content: String,
    mimeType: String
): McpServerFeatures.SyncResourceSpecification

Parameters:

  • uri: String - Resource URI (e.g., "app://docs/readme")
  • name: String - Human-readable name
  • description: String - Resource description
  • content: String - Static content
  • mimeType: String - MIME type (e.g., "text/markdown", "application/json")

Returns: SyncResourceSpecification ready for registration

Usage:

import com.embabel.agent.mcpserver.sync.SyncResourceSpecificationFactory
import com.embabel.agent.mcpserver.sync.McpResourcePublisher
import io.modelcontextprotocol.server.McpServerFeatures
import org.springframework.stereotype.Service

@Service
class DocumentationPublisher : McpResourcePublisher {

    override fun resources(): List<McpServerFeatures.SyncResourceSpecification> {
        return listOf(
            SyncResourceSpecificationFactory.staticSyncResourceSpecification(
                uri = "app://docs/api-reference",
                name = "APIReference",
                description = "Complete API reference documentation",
                content = loadApiDocs(),
                mimeType = "text/markdown"
            ),
            SyncResourceSpecificationFactory.staticSyncResourceSpecification(
                uri = "app://data/schema",
                name = "DataSchema",
                description = "JSON schema for data structures",
                content = loadSchema(),
                mimeType = "application/json"
            )
        )
    }

    override fun infoString(verbose: Boolean?, indent: Int) =
        "DocumentationPublisher: ${resources().size} resources"

    private fun loadApiDocs(): String = """
        # API Reference

        ## Authentication
        - POST /api/auth/login
        - POST /api/auth/logout
    """.trimIndent()

    private fun loadSchema(): String = """
        {
          "type": "object",
          "properties": {
            "id": {"type": "string"},
            "name": {"type": "string"}
          }
        }
    """.trimIndent()
}

Common MIME Types:

// Markdown documentation
mimeType = "text/markdown"

// JSON data
mimeType = "application/json"

// Plain text
mimeType = "text/plain"

// HTML content
mimeType = "text/html"

// XML data
mimeType = "application/xml"

syncResourceSpecification { .api }

Create a dynamic resource with on-demand content loading.

fun syncResourceSpecification(
    uri: String,
    name: String,
    description: String,
    resourceLoader: (McpSyncServerExchange) -> String,
    mimeType: String
): McpServerFeatures.SyncResourceSpecification

Parameters:

  • uri: String - Resource URI
  • name: String - Human-readable name
  • description: String - Resource description
  • resourceLoader: (McpSyncServerExchange) -> String - Lambda that loads content
  • mimeType: String - MIME type

Returns: SyncResourceSpecification with dynamic loading

Usage:

import io.modelcontextprotocol.server.McpSyncServerExchange
import com.fasterxml.jackson.databind.ObjectMapper

@Service
class StatusPublisher : McpResourcePublisher {

    private val objectMapper = ObjectMapper()

    override fun resources(): List<McpServerFeatures.SyncResourceSpecification> {
        return listOf(
            SyncResourceSpecificationFactory.syncResourceSpecification(
                uri = "app://status/current",
                name = "CurrentStatus",
                description = "Real-time system status",
                resourceLoader = { exchange: McpSyncServerExchange ->
                    val status = getCurrentStatus()
                    objectMapper.writeValueAsString(status)
                },
                mimeType = "application/json"
            ),
            SyncResourceSpecificationFactory.syncResourceSpecification(
                uri = "app://metrics/performance",
                name = "Performance",
                description = "Performance metrics",
                resourceLoader = { exchange ->
                    val metrics = collectMetrics()
                    objectMapper.writeValueAsString(metrics)
                },
                mimeType = "application/json"
            )
        )
    }

    override fun infoString(verbose: Boolean?, indent: Int) =
        "StatusPublisher: ${resources().size} resources"

    private fun getCurrentStatus(): Status {
        return Status(
            healthy = true,
            uptime = System.currentTimeMillis(),
            activeUsers = getActiveUserCount()
        )
    }

    private fun collectMetrics(): Metrics {
        return Metrics(
            cpu = getCpuUsage(),
            memory = getMemoryUsage(),
            requests = getRequestCount()
        )
    }

    private fun getActiveUserCount(): Int = 42
    private fun getCpuUsage(): Double = 45.5
    private fun getMemoryUsage(): Double = 67.8
    private fun getRequestCount(): Long = 1000
}

data class Status(val healthy: Boolean, val uptime: Long, val activeUsers: Int)
data class Metrics(val cpu: Double, val memory: Double, val requests: Long)

Exchange Usage:

SyncResourceSpecificationFactory.syncResourceSpecification(
    uri = "app://user/preferences",
    name = "UserPreferences",
    description = "User-specific preferences",
    resourceLoader = { exchange ->
        // Access request context from exchange if needed
        val userId = extractUserIdFromExchange(exchange)
        val prefs = loadUserPreferences(userId)
        objectMapper.writeValueAsString(prefs)
    },
    mimeType = "application/json"
)

Prompt Factories (Sync Mode)

McpPromptFactory { .api }

Factory for creating synchronous prompt specifications from Java/Kotlin types.

package com.embabel.agent.mcpserver.sync

import io.modelcontextprotocol.server.McpServerFeatures
import com.embabel.common.core.types.Named
import com.embabel.common.core.types.Described

class McpPromptFactory {

    fun syncPromptSpecificationForType(
        goal: Named & Described,
        inputType: Class<*>
    ): McpServerFeatures.SyncPromptSpecification
}

syncPromptSpecificationForType { .api }

Create prompt specification from input type class.

fun syncPromptSpecificationForType(
    goal: Named & Described,
    inputType: Class<*>
): McpServerFeatures.SyncPromptSpecification

Parameters:

  • goal: Named & Described - Goal object with name and description
  • inputType: Class<*> - Input type class (generates JSON schema)

Returns: SyncPromptSpecification for the goal

Usage:

import com.embabel.agent.mcpserver.sync.McpPromptFactory
import com.embabel.agent.mcpserver.sync.McpPromptPublisher
import com.embabel.common.core.types.Named
import com.embabel.common.core.types.Described
import com.fasterxml.jackson.annotation.JsonPropertyDescription
import io.modelcontextprotocol.server.McpServerFeatures
import org.springframework.stereotype.Service

@Service
class TaskPromptsPublisher : McpPromptPublisher {

    private val factory = McpPromptFactory()

    override fun prompts(): List<McpServerFeatures.SyncPromptSpecification> {
        return listOf(
            createTaskPrompt(),
            updateTaskPrompt(),
            searchTasksPrompt()
        )
    }

    override fun infoString(verbose: Boolean?, indent: Int) =
        "TaskPromptsPublisher: ${prompts().size} prompts"

    private fun createTaskPrompt(): McpServerFeatures.SyncPromptSpecification {
        val goal = object : Named, Described {
            override val name = "createTask"
            override val description = "Create a new task in the system"
        }

        return factory.syncPromptSpecificationForType(
            goal = goal,
            inputType = CreateTaskInput::class.java
        )
    }

    private fun updateTaskPrompt(): McpServerFeatures.SyncPromptSpecification {
        val goal = object : Named, Described {
            override val name = "updateTask"
            override val description = "Update an existing task"
        }

        return factory.syncPromptSpecificationForType(
            goal = goal,
            inputType = UpdateTaskInput::class.java
        )
    }

    private fun searchTasksPrompt(): McpServerFeatures.SyncPromptSpecification {
        val goal = object : Named, Described {
            override val name = "searchTasks"
            override val description = "Search for tasks matching criteria"
        }

        return factory.syncPromptSpecificationForType(
            goal = goal,
            inputType = SearchTasksInput::class.java
        )
    }
}

data class CreateTaskInput(
    @JsonPropertyDescription("Title of the task (max 100 characters)")
    val title: String,

    @JsonPropertyDescription("Detailed description of the task")
    val description: String,

    @JsonPropertyDescription("Priority level (1-5, where 5 is highest)")
    val priority: Int,

    @JsonPropertyDescription("Due date in ISO 8601 format (optional)")
    val dueDate: String? = null
)

data class UpdateTaskInput(
    @JsonPropertyDescription("ID of task to update")
    val taskId: String,

    @JsonPropertyDescription("New title (optional)")
    val title: String? = null,

    @JsonPropertyDescription("New description (optional)")
    val description: String? = null,

    @JsonPropertyDescription("New priority (optional)")
    val priority: Int? = null
)

data class SearchTasksInput(
    @JsonPropertyDescription("Search query string")
    val query: String,

    @JsonPropertyDescription("Filter by status (optional)")
    val status: String? = null,

    @JsonPropertyDescription("Filter by priority (optional)")
    val priority: Int? = null
)

Input Type Design:

// Simple input
data class SimpleInput(
    @JsonPropertyDescription("The operation to perform")
    val operation: String
)

// Nested types
data class ComplexInput(
    @JsonPropertyDescription("User information")
    val user: UserInfo,

    @JsonPropertyDescription("Processing options")
    val options: ProcessingOptions
)

data class UserInfo(
    @JsonPropertyDescription("User ID")
    val id: String,

    @JsonPropertyDescription("User name")
    val name: String
)

data class ProcessingOptions(
    @JsonPropertyDescription("Enable verbose output")
    val verbose: Boolean = false,

    @JsonPropertyDescription("Maximum items to process")
    val limit: Int = 100
)

// Collections
data class BatchInput(
    @JsonPropertyDescription("List of item IDs")
    val items: List<String>,

    @JsonPropertyDescription("Configuration parameters")
    val config: Map<String, String> = emptyMap()
)

Prompt Factories (Async Mode)

McpAsyncPromptFactory { .api }

Factory for creating asynchronous prompt specifications.

package com.embabel.agent.mcpserver.async

import io.modelcontextprotocol.server.McpServerFeatures
import com.embabel.common.core.types.Named
import com.embabel.common.core.types.Described

class McpAsyncPromptFactory {

    fun asyncPromptSpecificationForType(
        goal: Named & Described,
        inputType: Class<*>
    ): McpServerFeatures.AsyncPromptSpecification
}

asyncPromptSpecificationForType { .api }

Create async prompt specification from input type class.

fun asyncPromptSpecificationForType(
    goal: Named & Described,
    inputType: Class<*>
): McpServerFeatures.AsyncPromptSpecification

Parameters:

  • goal: Named & Described - Goal object with name and description
  • inputType: Class<*> - Input type class

Returns: AsyncPromptSpecification for the goal

Usage:

import com.embabel.agent.mcpserver.async.McpAsyncPromptPublisher
import com.embabel.agent.mcpserver.async.McpAsyncPromptFactory
import com.embabel.common.core.types.Named
import com.embabel.common.core.types.Described
import com.fasterxml.jackson.annotation.JsonPropertyDescription
import io.modelcontextprotocol.server.McpServerFeatures
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Service

@Service
@ConditionalOnProperty(
    value = ["spring.ai.mcp.server.type"],
    havingValue = "ASYNC"
)
class AsyncAnalysisPromptsPublisher : McpAsyncPromptPublisher {

    private val factory = McpAsyncPromptFactory()

    override fun prompts(): List<McpServerFeatures.AsyncPromptSpecification> {
        return listOf(
            analyzeDataPrompt(),
            generateReportPrompt()
        )
    }

    override fun infoString(verbose: Boolean?, indent: Int) =
        "AsyncAnalysisPromptsPublisher: ${prompts().size} prompts"

    private fun analyzeDataPrompt(): McpServerFeatures.AsyncPromptSpecification {
        val goal = object : Named, Described {
            override val name = "analyzeData"
            override val description = "Perform asynchronous data analysis"
        }

        return factory.asyncPromptSpecificationForType(
            goal = goal,
            inputType = AnalysisInput::class.java
        )
    }

    private fun generateReportPrompt(): McpServerFeatures.AsyncPromptSpecification {
        val goal = object : Named, Described {
            override val name = "generateReport"
            override val description = "Generate comprehensive report"
        }

        return factory.asyncPromptSpecificationForType(
            goal = goal,
            inputType = ReportInput::class.java
        )
    }
}

data class AnalysisInput(
    @JsonPropertyDescription("Data source identifier")
    val dataSource: String,

    @JsonPropertyDescription("Type of analysis to perform")
    val analysisType: String,

    @JsonPropertyDescription("Time range for analysis")
    val timeRange: String? = null
)

data class ReportInput(
    @JsonPropertyDescription("Report type")
    val reportType: String,

    @JsonPropertyDescription("Output format (pdf, excel, html)")
    val format: String = "pdf",

    @JsonPropertyDescription("Include charts")
    val includeCharts: Boolean = true
)

Common Patterns

Multi-Resource Publisher { .api }

@Service
class SystemResourcesPublisher : McpResourcePublisher {

    override fun resources(): List<McpServerFeatures.SyncResourceSpecification> {
        return listOf(
            createHealthResource(),
            createMetricsResource(),
            createConfigResource()
        )
    }

    override fun infoString(verbose: Boolean?, indent: Int) =
        "SystemResourcesPublisher: ${resources().size} resources"

    private fun createHealthResource() =
        SyncResourceSpecificationFactory.staticSyncResourceSpecification(
            uri = "app://system/health",
            name = "Health",
            description = "System health status",
            content = """{"status": "healthy", "uptime": 1000}""",
            mimeType = "application/json"
        )

    private fun createMetricsResource() =
        SyncResourceSpecificationFactory.syncResourceSpecification(
            uri = "app://system/metrics",
            name = "Metrics",
            description = "Current system metrics",
            resourceLoader = { getMetrics() },
            mimeType = "application/json"
        )

    private fun createConfigResource() =
        SyncResourceSpecificationFactory.staticSyncResourceSpecification(
            uri = "app://system/config",
            name = "Configuration",
            description = "System configuration",
            content = loadConfiguration(),
            mimeType = "application/json"
        )

    private fun getMetrics(): String = """{"cpu": 45, "memory": 67}"""
    private fun loadConfiguration(): String = """{"debug": false, "timeout": 30}"""
}

Multi-Prompt Publisher { .api }

@Service
class WorkflowPromptsPublisher : McpPromptPublisher {

    private val factory = McpPromptFactory()

    override fun prompts(): List<McpServerFeatures.SyncPromptSpecification> {
        return listOf(
            createWorkflowPrompt(),
            updateWorkflowPrompt(),
            executeWorkflowPrompt()
        )
    }

    override fun infoString(verbose: Boolean?, indent: Int) =
        "WorkflowPromptsPublisher: ${prompts().size} prompts"

    private fun createWorkflowPrompt() = factory.syncPromptSpecificationForType(
        goal = namedGoal("createWorkflow", "Create a new workflow"),
        inputType = CreateWorkflowInput::class.java
    )

    private fun updateWorkflowPrompt() = factory.syncPromptSpecificationForType(
        goal = namedGoal("updateWorkflow", "Update existing workflow"),
        inputType = UpdateWorkflowInput::class.java
    )

    private fun executeWorkflowPrompt() = factory.syncPromptSpecificationForType(
        goal = namedGoal("executeWorkflow", "Execute a workflow"),
        inputType = ExecuteWorkflowInput::class.java
    )

    private fun namedGoal(name: String, description: String) = object : Named, Described {
        override val name = name
        override val description = description
    }
}

data class CreateWorkflowInput(
    @JsonPropertyDescription("Workflow name")
    val name: String,
    @JsonPropertyDescription("Workflow steps")
    val steps: List<String>
)

data class UpdateWorkflowInput(
    @JsonPropertyDescription("Workflow ID")
    val workflowId: String,
    @JsonPropertyDescription("Updated steps")
    val steps: List<String>
)

data class ExecuteWorkflowInput(
    @JsonPropertyDescription("Workflow ID")
    val workflowId: String,
    @JsonPropertyDescription("Input parameters")
    val parameters: Map<String, Any>
)

Best Practices

  1. Use Static Resources for Fixed Content

    // Good: Static content loaded once
    staticSyncResourceSpecification(
        uri = "app://docs/readme",
        name = "README",
        description = "Project documentation",
        content = readmeContent,
        mimeType = "text/markdown"
    )
  2. Use Dynamic Resources for Real-Time Data

    // Good: Content loaded on each request
    syncResourceSpecification(
        uri = "app://status/current",
        name = "Status",
        description = "Current status",
        resourceLoader = { getCurrentStatus() },
        mimeType = "application/json"
    )
  3. Document All Input Fields

    // Good: Clear descriptions
    data class Input(
        @JsonPropertyDescription("User ID (required)")
        val userId: String,
    
        @JsonPropertyDescription("Optional comment")
        val comment: String? = null
    )
  4. Use Appropriate MIME Types

    // Match content type
    mimeType = "text/markdown"     // For Markdown
    mimeType = "application/json"  // For JSON
    mimeType = "text/plain"        // For plain text
  5. Handle Errors Gracefully

    resourceLoader = { exchange ->
        try {
            loadData()
        } catch (e: Exception) {
            logger.error("Failed to load resource", e)
            """{"error": "Data unavailable"}"""
        }
    }

See Also

  • Resources and Prompts Guide - Comprehensive examples
  • Publishers API - Publisher interfaces
  • Creating Publishers Guide - Publisher patterns
  • Execution Modes Guide - Mode selection
tessl i tessl/maven-com-embabel-agent--embabel-agent-mcpserver@0.3.1

docs

index.md

tile.json