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

extensions.mddocs/api/

Extensions API

API reference for Kotlin extension functions that provide convenient utility methods for working with MCP server components.

Package

com.embabel.agent.mcpserver.extensions

Overview

Extension functions add utility methods to existing types without modifying their source code. These extensions simplify common operations on tool callbacks, registries, and publishers.

Tool Extensions

ToolCallback.toolNames() { .api }

Extract tool names from a collection of tool callbacks.

package com.embabel.agent.mcpserver.extensions

fun List<ToolCallback>.toolNames(): List<String> {
    return this.map { it.name }
}

Receiver: List<ToolCallback>

Returns: List<String> - List of tool names

Usage:

import com.embabel.agent.mcpserver.extensions.toolNames
import org.springframework.ai.tool.ToolCallback

@Service
class ToolNameService(
    private val toolRegistry: ToolRegistry
) {

    fun getAllToolNames(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks -> callbacks.toolNames() }
    }

    fun printToolNames() {
        toolRegistry.listToolCallbacks()
            .subscribe { callbacks ->
                val names = callbacks.toolNames()
                println("Registered tools: ${names.joinToString()}")
            }
    }

    fun countToolsWithPrefix(prefix: String): Mono<Int> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks.toolNames()
                    .count { it.startsWith(prefix) }
            }
    }
}

Combined with Filtering:

@Service
class FilteredToolNameService(
    private val toolRegistry: ToolRegistry
) {

    fun getApiToolNames(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks
                    .filter { it.name.startsWith("api_") }
                    .toolNames()
            }
    }

    fun getToolNamesByPattern(pattern: Regex): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks
                    .filter { it.name.matches(pattern) }
                    .toolNames()
            }
    }

    fun getSortedToolNames(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks.toolNames().sorted()
            }
    }
}

Publisher Usage:

@Service
class MyToolPublisher : McpExportToolCallbackPublisher {

    override val toolCallbacks: List<ToolCallback>
        get() = McpToolExport.fromToolObject(
            ToolObject(
                objects = listOf(tool1, tool2, tool3),
                namingStrategy = { "api_$it" }
            )
        ).toolCallbacks

    override fun infoString(verbose: Boolean?, indent: Int): String {
        val names = toolCallbacks.toolNames()
        return if (verbose == true) {
            "MyToolPublisher: ${toolCallbacks.size} tools\n  - ${names.joinToString("\n  - ")}"
        } else {
            "MyToolPublisher: ${toolCallbacks.size} tools"
        }
    }

    private val tool1: Any = TODO()
    private val tool2: Any = TODO()
    private val tool3: Any = TODO()
}

Collection Extensions

List<ToolCallback>.filterByPrefix { .api }

Filter tool callbacks by name prefix.

fun List<ToolCallback>.filterByPrefix(prefix: String): List<ToolCallback> {
    return this.filter { it.name.startsWith(prefix) }
}

Receiver: List<ToolCallback>

Parameters:

  • prefix: String - Prefix to match

Returns: List<ToolCallback> - Filtered list

Usage:

import com.embabel.agent.mcpserver.extensions.filterByPrefix

@Service
class PrefixFilterService(
    private val toolRegistry: ToolRegistry
) {

    fun getApiTools(): Mono<List<ToolCallback>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks -> callbacks.filterByPrefix("api_") }
    }

    fun getUtilityTools(): Mono<List<ToolCallback>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks -> callbacks.filterByPrefix("util_") }
    }

    fun countToolsByPrefix(prefix: String): Mono<Int> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks -> callbacks.filterByPrefix(prefix).size }
    }
}

List<ToolCallback>.groupByPrefix { .api }

Group tool callbacks by their name prefix (up to first underscore).

fun List<ToolCallback>.groupByPrefix(): Map<String, List<ToolCallback>> {
    return this.groupBy { callback ->
        callback.name.substringBefore('_')
    }
}

Receiver: List<ToolCallback>

Returns: Map<String, List<ToolCallback>> - Tools grouped by prefix

Usage:

import com.embabel.agent.mcpserver.extensions.groupByPrefix

@Service
class ToolGroupingService(
    private val toolRegistry: ToolRegistry
) {

    fun getToolsByModule(): Mono<Map<String, List<ToolCallback>>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks -> callbacks.groupByPrefix() }
    }

    fun countToolsByModule(): Mono<Map<String, Int>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks.groupByPrefix()
                    .mapValues { (_, tools) -> tools.size }
            }
    }

    fun printModuleSummary() {
        toolRegistry.listToolCallbacks()
            .subscribe { callbacks ->
                val grouped = callbacks.groupByPrefix()
                println("Tools by module:")
                grouped.forEach { (prefix, tools) ->
                    println("  $prefix: ${tools.size} tools")
                }
            }
    }
}

List<ToolCallback>.hasToolNamed { .api }

Check if a tool with given name exists in the list.

fun List<ToolCallback>.hasToolNamed(toolName: String): Boolean {
    return this.any { it.name == toolName }
}

Receiver: List<ToolCallback>

Parameters:

  • toolName: String - Tool name to check

Returns: Boolean - True if tool exists

Usage:

import com.embabel.agent.mcpserver.extensions.hasToolNamed

@Service
class ToolExistenceService(
    private val toolRegistry: ToolRegistry
) {

    fun checkToolExists(toolName: String): Mono<Boolean> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks -> callbacks.hasToolNamed(toolName) }
    }

    fun checkMultipleToolsExist(toolNames: List<String>): Mono<Map<String, Boolean>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                toolNames.associateWith { name ->
                    callbacks.hasToolNamed(name)
                }
            }
    }

    fun allToolsExist(toolNames: List<String>): Mono<Boolean> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                toolNames.all { name -> callbacks.hasToolNamed(name) }
            }
    }
}

String Extensions

String.toToolName { .api }

Sanitize string to valid tool name format.

fun String.toToolName(): String {
    return this
        .replace(Regex("[^a-zA-Z0-9_]"), "_")
        .toLowerCase()
        .take(50)
}

Receiver: String

Returns: String - Sanitized tool name

Usage:

import com.embabel.agent.mcpserver.extensions.toToolName

// Sanitize user input
val userInput = "My API Tool!"
val toolName = userInput.toToolName()  // "my_api_tool_"

// Generate tool names
val displayName = "User Management (Admin)"
val toolName = displayName.toToolName()  // "user_management__admin_"

// Validate and sanitize
fun createToolName(input: String): String {
    val sanitized = input.toToolName()
    require(sanitized.isNotBlank()) { "Invalid tool name" }
    return sanitized
}

String.isPrefixOf { .api }

Check if string is a prefix of tool name.

fun String.isPrefixOf(toolName: String): Boolean {
    return toolName.startsWith(this)
}

Receiver: String

Parameters:

  • toolName: String - Tool name to check

Returns: Boolean - True if receiver is prefix

Usage:

import com.embabel.agent.mcpserver.extensions.isPrefixOf

@Service
class PrefixCheckService {

    fun findToolsWithPrefix(prefix: String): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks
                    .filter { prefix.isPrefixOf(it.name) }
                    .map { it.name }
            }
    }

    fun hasToolsWithPrefix(prefix: String): Mono<Boolean> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks.any { prefix.isPrefixOf(it.name) }
            }
    }
}

Publisher Extensions

McpExportToolCallbackPublisher.toolCount { .api }

Get number of tools from publisher.

val McpExportToolCallbackPublisher.toolCount: Int
    get() = this.toolCallbacks.size

Receiver: McpExportToolCallbackPublisher

Returns: Int - Number of tools

Usage:

import com.embabel.agent.mcpserver.extensions.toolCount

@Service
class PublisherAnalyzer(
    private val publishers: List<McpExportToolCallbackPublisher>
) {

    fun countAllTools(): Int {
        return publishers.sumOf { it.toolCount }
    }

    fun findLargestPublisher(): McpExportToolCallbackPublisher? {
        return publishers.maxByOrNull { it.toolCount }
    }

    fun printPublisherSummary() {
        publishers.forEach { publisher ->
            println("${publisher::class.simpleName}: ${publisher.toolCount} tools")
        }
    }

    fun hasEmptyPublishers(): Boolean {
        return publishers.any { it.toolCount == 0 }
    }
}

McpResourcePublisher.resourceCount { .api }

Get number of resources from publisher.

val McpResourcePublisher.resourceCount: Int
    get() = this.resources().size

Receiver: McpResourcePublisher

Returns: Int - Number of resources

Usage:

import com.embabel.agent.mcpserver.extensions.resourceCount

@Service
class ResourceAnalyzer(
    private val publishers: List<McpResourcePublisher>
) {

    fun countAllResources(): Int {
        return publishers.sumOf { it.resourceCount }
    }

    fun findPublisherWithMostResources(): McpResourcePublisher? {
        return publishers.maxByOrNull { it.resourceCount }
    }

    fun hasResourcePublishers(): Boolean {
        return publishers.any { it.resourceCount > 0 }
    }
}

Reactive Extensions

Mono<List<ToolCallback>>.extractNames { .api }

Extract tool names from Mono of tool callbacks.

fun Mono<List<ToolCallback>>.extractNames(): Mono<List<String>> {
    return this.map { callbacks -> callbacks.toolNames() }
}

Receiver: Mono<List<ToolCallback>>

Returns: Mono<List<String>> - Mono of tool names

Usage:

import com.embabel.agent.mcpserver.extensions.extractNames

@Service
class ReactiveToolService(
    private val toolRegistry: ToolRegistry
) {

    fun getToolNames(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .extractNames()
    }

    fun printToolNames() {
        toolRegistry.listToolCallbacks()
            .extractNames()
            .subscribe { names ->
                println("Tools: ${names.joinToString()}")
            }
    }
}

Flux<ToolCallback>.toNamesList { .api }

Convert Flux of tool callbacks to list of names.

fun Flux<ToolCallback>.toNamesList(): Mono<List<String>> {
    return this.map { it.name }.collectList()
}

Receiver: Flux<ToolCallback>

Returns: Mono<List<String>> - Mono of tool names

Usage:

import com.embabel.agent.mcpserver.extensions.toNamesList

@Service
class FluxToolService(
    private val toolRegistry: ToolRegistry
) {

    fun getFilteredToolNames(prefix: String): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .flatMapMany { Flux.fromIterable(it) }
            .filter { it.name.startsWith(prefix) }
            .toNamesList()
    }

    fun getSortedToolNames(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .flatMapMany { Flux.fromIterable(it) }
            .sort { a, b -> a.name.compareTo(b.name) }
            .toNamesList()
    }
}

Validation Extensions

ToolCallback.isValid { .api }

Check if tool callback is valid (has name and description).

fun ToolCallback.isValid(): Boolean {
    return this.name.isNotBlank() && this.description.isNotBlank()
}

Receiver: ToolCallback

Returns: Boolean - True if valid

Usage:

import com.embabel.agent.mcpserver.extensions.isValid

@Service
class ValidationService(
    private val toolRegistry: ToolRegistry
) {

    fun validateAllTools(): Mono<ValidationResult> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                val invalid = callbacks.filterNot { it.isValid() }
                if (invalid.isEmpty()) {
                    ValidationResult.success("All tools valid")
                } else {
                    ValidationResult.failure("Invalid tools: ${invalid.map { it.name }}")
                }
            }
    }

    fun findInvalidTools(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks
                    .filterNot { it.isValid() }
                    .map { it.name }
            }
    }
}

data class ValidationResult(val valid: Boolean, val message: String) {
    companion object {
        fun success(msg: String) = ValidationResult(true, msg)
        fun failure(msg: String) = ValidationResult(false, msg)
    }
}

ToolCallback.hasSchema { .api }

Check if tool has input schema defined.

fun ToolCallback.hasSchema(): Boolean {
    return this.inputTypeSchema != null
}

Receiver: ToolCallback

Returns: Boolean - True if schema exists

Usage:

import com.embabel.agent.mcpserver.extensions.hasSchema

@Service
class SchemaAnalyzer(
    private val toolRegistry: ToolRegistry
) {

    fun countToolsWithSchema(): Mono<Int> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks.count { it.hasSchema() }
            }
    }

    fun findToolsWithoutSchema(): Mono<List<String>> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                callbacks
                    .filterNot { it.hasSchema() }
                    .map { it.name }
            }
    }

    fun getSchemaCompleteness(): Mono<Double> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                if (callbacks.isEmpty()) 0.0
                else callbacks.count { it.hasSchema() }.toDouble() / callbacks.size
            }
    }
}

Usage Examples

Complete Tool Analysis { .api }

import com.embabel.agent.mcpserver.extensions.*

@Service
class ToolAnalysisService(
    private val toolRegistry: ToolRegistry
) {

    fun generateToolReport(): Mono<ToolReport> {
        return toolRegistry.listToolCallbacks()
            .map { callbacks ->
                ToolReport(
                    totalTools = callbacks.size,
                    toolNames = callbacks.toolNames(),
                    byPrefix = callbacks.groupByPrefix()
                        .mapValues { it.value.size },
                    validTools = callbacks.count { it.isValid() },
                    invalidTools = callbacks.filterNot { it.isValid() }.size,
                    withSchema = callbacks.count { it.hasSchema() },
                    withoutSchema = callbacks.filterNot { it.hasSchema() }.size
                )
            }
    }

    fun printDetailedReport() {
        generateToolReport()
            .subscribe { report ->
                println("=== Tool Report ===")
                println("Total: ${report.totalTools}")
                println("\nBy Prefix:")
                report.byPrefix.forEach { (prefix, count) ->
                    println("  $prefix: $count")
                }
                println("\nValidation:")
                println("  Valid: ${report.validTools}")
                println("  Invalid: ${report.invalidTools}")
                println("\nSchema:")
                println("  With schema: ${report.withSchema}")
                println("  Without schema: ${report.withoutSchema}")
            }
    }
}

data class ToolReport(
    val totalTools: Int,
    val toolNames: List<String>,
    val byPrefix: Map<String, Int>,
    val validTools: Int,
    val invalidTools: Int,
    val withSchema: Int,
    val withoutSchema: Int
)

Publisher Enhancement { .api }

import com.embabel.agent.mcpserver.extensions.*

@Service
class EnhancedPublisher : McpExportToolCallbackPublisher {

    override val toolCallbacks: List<ToolCallback>
        get() = McpToolExport.fromToolObject(
            ToolObject(
                objects = listOf(tool1, tool2, tool3),
                namingStrategy = { "api_$it" }
            )
        ).toolCallbacks

    override fun infoString(verbose: Boolean?, indent: Int): String {
        val names = toolCallbacks.toolNames()
        val grouped = toolCallbacks.groupByPrefix()
        val valid = toolCallbacks.count { it.isValid() }
        val withSchema = toolCallbacks.count { it.hasSchema() }

        return if (verbose == true) {
            buildString {
                appendLine("EnhancedPublisher:")
                appendLine("  Total: ${toolCallbacks.size}")
                appendLine("  Valid: $valid")
                appendLine("  With schema: $withSchema")
                appendLine("  By prefix:")
                grouped.forEach { (prefix, tools) ->
                    appendLine("    $prefix: ${tools.size}")
                }
                appendLine("  Tools:")
                names.forEach { name ->
                    appendLine("    - $name")
                }
            }
        } else {
            "EnhancedPublisher: ${toolCallbacks.size} tools"
        }
    }

    private val tool1: Any = TODO()
    private val tool2: Any = TODO()
    private val tool3: Any = TODO()
}

Best Practices

  1. Import Extensions: Import specific extensions you need

    import com.embabel.agent.mcpserver.extensions.toolNames
    import com.embabel.agent.mcpserver.extensions.filterByPrefix
  2. Chain Extensions: Combine multiple extensions

    callbacks
        .filterByPrefix("api_")
        .filter { it.hasSchema() }
        .toolNames()
  3. Use with Reactive Streams: Integrate with Mono/Flux

    toolRegistry.listToolCallbacks()
        .map { it.toolNames() }
        .subscribe { names -> println(names) }
  4. Validate Before Use: Check validity with extensions

    if (callback.isValid() && callback.hasSchema()) {
        useTool(callback)
    }
  5. Enhanced Logging: Use extensions for better info strings

    override fun infoString(verbose: Boolean?, indent: Int): String {
        val names = toolCallbacks.toolNames()
        return "Publisher: ${names.joinToString()}"
    }

See Also

  • Tool Registry API - Tool query operations
  • Publishers API - Publisher interfaces
  • Tool Export API - Tool export methods
  • Dynamic Management Guide - Runtime operations
tessl i tessl/maven-com-embabel-agent--embabel-agent-mcpserver@0.3.1

docs

index.md

tile.json