Skip to main content

Overview

ToolRegistry serves as a central repository for all tools available to an agent. It provides functionality to register, retrieve, and merge collections of tools with type-safe access patterns.
The registry ensures tool name uniqueness and provides both name-based and type-based tool lookup.

Class Definition

public class ToolRegistry private constructor(
    tools: List<Tool<*, *>> = emptyList()
)

Properties

tools
List<Tool<*, *>>
Immutable list of tools currently registered. Returns a copy to prevent external modification.

Methods

getToolOrNull

Retrieves a tool by its name, or null if not found.
public fun getToolOrNull(toolName: String): Tool<*, *>?
toolName
String
required
The name of the tool to retrieve
return
Tool<*, *>?
The tool with the specified name, or null if not found

getTool (by name)

Retrieves a tool by its name.
public fun getTool(toolName: String): Tool<*, *>
toolName
String
required
The name of the tool to retrieve
return
Tool<*, *>
The tool with the specified name
throws
IllegalArgumentException
If no tool with the specified name is found

getTool (by type)

Retrieves a tool by its type.
public inline fun <reified T : Tool<*, *>> getTool(): T
T
Tool<*, *>
required
The type of tool to retrieve
return
T
The tool of the specified type
throws
IllegalArgumentException
If no tool of the specified type is found

plus

Combines tools from this registry with another registry.
public operator fun plus(toolRegistry: ToolRegistry): ToolRegistry
toolRegistry
ToolRegistry
required
The other ToolRegistry to merge with
return
ToolRegistry
A new ToolRegistry containing the combined list of tools (deduplicated by name)

add

Adds a tool to the registry if not already present.
public fun add(tool: Tool<*, *>)
tool
Tool<*, *>
required
The tool to be added
ToolRegistry is internally mutable but should be treated as immutable after creation. Use the builder pattern or + operator for combining registries.

addAll

Adds multiple tools to the registry.
public fun addAll(vararg tools: Tool<*, *>)
tools
vararg Tool<*, *>
required
The tools to be added

Factory Methods

invoke (DSL)

Creates a registry using a builder DSL.
public operator fun invoke(
    init: Builder.() -> Unit
): ToolRegistry
init
Builder.() -> Unit
required
Lambda that configures the registry by adding tools
return
ToolRegistry
A new configured ToolRegistry instance

builder (Java API)

Creates a builder for fluent configuration.
public fun builder(): ToolRegistryBuilder
return
ToolRegistryBuilder
A new builder instance for constructing a ToolRegistry

EMPTY

Constant representing an empty registry with no tools.
public val EMPTY: ToolRegistry

Usage Examples

Basic Registry Creation

val registry = ToolRegistry {
    tool(CalculatorTool())
    tool(WebSearchTool())
    tool(WeatherTool())
}

Retrieve Tool by Name

val registry = ToolRegistry {
    tool(CalculatorTool())
    tool(SearchTool())
}

// Safe retrieval
val tool = registry.getToolOrNull("calculator")
if (tool != null) {
    println("Found tool: ${tool.name}")
}

// Direct retrieval (throws if not found)
try {
    val searchTool = registry.getTool("web_search")
    println("Found: ${searchTool.name}")
} catch (e: IllegalArgumentException) {
    println("Tool not found")
}

Retrieve Tool by Type

val registry = ToolRegistry {
    tool(CalculatorTool())
    tool(WeatherTool())
}

// Type-safe retrieval
val calculator = registry.getTool<CalculatorTool>()
println("Calculator tool: ${calculator.name}")

// Will throw if tool not registered
try {
    val search = registry.getTool<WebSearchTool>()
} catch (e: IllegalArgumentException) {
    println("WebSearchTool not in registry")
}

Merge Registries

val baseTools = ToolRegistry {
    tool(CalculatorTool())
    tool(DateTimeTool())
}

val searchTools = ToolRegistry {
    tool(WebSearchTool())
    tool(DatabaseSearchTool())
}

// Combine registries
val allTools = baseTools + searchTools

println("Total tools: ${allTools.tools.size}")
// Output: Total tools: 4

Conditional Tool Registration

val registry = ToolRegistry {
    tool(CalculatorTool())
    
    if (hasInternetAccess) {
        tool(WebSearchTool())
        tool(ApiCallTool())
    }
    
    if (hasFileAccess) {
        tool(ReadFileTool())
        tool(WriteFileTool())
    }
}

Dynamic Tool Lists

val toolInstances = listOf(
    CalculatorTool(),
    WeatherTool(),
    SearchTool()
)

val registry = ToolRegistry {
    tools(toolInstances)
}

Builder Pattern (Java API)

val registry = ToolRegistry.builder()
    .tool(CalculatorTool())
    .tool(WeatherTool())
    .tool(SearchTool())
    .build()

Iterate Over Tools

val registry = ToolRegistry {
    tool(CalculatorTool())
    tool(WeatherTool())
    tool(SearchTool())
}

// Access all tools
registry.tools.forEach { tool ->
    println("Tool: ${tool.name} - ${tool.descriptor.description}")
}

Registry with Agent

val toolRegistry = ToolRegistry {
    tool(CalculatorTool())
    tool(WebSearchTool())
    tool(WeatherTool())
}

val agent = AIAgent(
    promptExecutor = executor,
    agentConfig = config,
    strategy = strategy,
    toolRegistry = toolRegistry
)

val result = agent.run("What's 15 * 24?")
println(result)

Empty Registry

// Agent with no tools
val agent = AIAgent(
    promptExecutor = executor,
    agentConfig = config,
    strategy = strategy,
    toolRegistry = ToolRegistry.EMPTY
)

Modular Tool Organization

object ToolSets {
    val math = ToolRegistry {
        tool(CalculatorTool())
        tool(StatisticsTool())
        tool(GeometryTool())
    }
    
    val web = ToolRegistry {
        tool(WebSearchTool())
        tool(HttpRequestTool())
        tool(WebScraperTool())
    }
    
    val files = ToolRegistry {
        tool(ReadFileTool())
        tool(WriteFileTool())
        tool(ListDirectoryTool())
    }
}

// Combine as needed
val fullRegistry = ToolSets.math + ToolSets.web
val readOnlyRegistry = ToolSets.web + ToolRegistry {
    tool(ReadFileTool())
}

Tool Registry Inspection

val registry = ToolRegistry {
    tool(CalculatorTool())
    tool(WeatherTool())
    tool(SearchTool())
}

// Check if tool exists
if (registry.getToolOrNull("calculator") != null) {
    println("Calculator available")
}

// Get all tool names
val toolNames = registry.tools.map { it.name }
println("Available tools: ${toolNames.joinToString(", ")}")

// Get all tool descriptors for LLM
val descriptors = registry.tools.map { it.descriptor }

Builder API

The Builder class provides methods for constructing registries:
class Builder {
    fun tool(tool: Tool<*, *>)
    fun tools(toolsList: List<Tool<*, *>>)
    fun build(): ToolRegistry
}

Builder Usage

val builder = ToolRegistry.Builder()
builder.tool(CalculatorTool())
builder.tool(WeatherTool())

if (enableSearch) {
    builder.tool(SearchTool())
}

val registry = builder.build()

Platform-Specific Features

JVM: Reflection-Based Registration

// Register tools from annotated methods
class MyToolSet {
    @Tool("calculator", "Performs calculations")
    fun calculate(operation: String, a: Int, b: Int): Int {
        // Implementation
    }
    
    @Tool("search", "Searches the web")
    fun search(query: String): String {
        // Implementation
    }
}

val toolSet = MyToolSet()
val registry = toolSet.asToolRegistry()

MCP (Model Context Protocol) Integration

// Register tools from MCP server
val mcpRegistry = McpToolRegistryProvider.fromServer(
    serverUrl = "http://localhost:3000"
)

val combinedRegistry = localTools + mcpRegistry

Best Practices

Registry Organization
  • Group related tools into logical sets
  • Use the + operator to compose registries
  • Keep registries immutable after creation when possible
  • Name tools consistently (lowercase with underscores)
  • Ensure tool names are unique within a registry
Performance Considerations
  • Tool lookup by type uses reflection and is O(n)
  • Tool lookup by name is O(n) in the current implementation
  • Avoid creating multiple registries unnecessarily
  • Cache registry instances when reusing tool sets

Testing

Mock Registry for Testing

val mockRegistry = ToolRegistry {
    tool(object : SimpleTool<Unit>(
        argsSerializer = Unit.serializer(),
        name = "test_tool",
        description = "Test tool"
    ) {
        override suspend fun execute(args: Unit): String {
            return "Test result"
        }
    })
}

val agent = AIAgent(
    promptExecutor = mockExecutor,
    agentConfig = config,
    strategy = strategy,
    toolRegistry = mockRegistry
)

Source Reference

Defined in: agents-tools/src/commonMain/kotlin/ai/koog/agents/core/tools/ToolRegistry.kt

Build docs developers (and LLMs) love