How to Expose Domain Object Methods as LLM Tools in Embabel
You expose domain object methods as LLM tools by annotating them with @Tool and letting the ToolRegistry automatically bundle them into ToolGroupMetadata that the Embabel runtime surfaces to language models.
The embabel/embabel-agent framework provides a type-safe bridge between your Java domain layer and Large Language Models (LLMs). By leveraging the @Tool annotation and Spring bean registration, you can expose domain object methods as LLM tools without boilerplate code.
The Three-Step Process
Annotate Methods with @Tool
Mark any public method you want the LLM to invoke with the @Tool annotation from com.embabel.agent.api.annotation.Tool. This annotation signals Embabel to generate a ToolDefinition containing the method name, parameter schema, and return type.
The annotation resides in embabel-agent-api/src/main/java/com/embabel/agent/api/annotation/Tool.java. When the Spring context starts, Embabel scans for beans containing methods annotated with @Tool and creates metadata objects describing each method's signature.
Register the Domain Bean
Embabel automatically registers any Spring bean containing @Tool methods with the ToolRegistry located in embabel-agent-core/src/main/java/com/embabel/agent/core/ToolRegistry.java. The registry scans the application context during startup, detects annotated methods, and prepares them for LLM invocation.
No explicit registration code is required if you use Spring's @Component or @Service stereotypes. The ToolRegistry discovers the bean and extracts its tool definitions automatically.
Tool Group Metadata Generation
The ToolRegistry bundles all ToolDefinition instances from a single bean into a ToolGroupMetadata object. This class, found in embabel-agent-observability/src/main/java/com/embabel/agent/core/ToolGroupMetadata.java, holds the bean reference, list of tool definitions, and runtime metadata such as version and description.
During chat processing, Embabel attaches this metadata to the LLM request payload under the tool_groups field, enabling the model to discover and invoke available methods.
How Tool Invocation Works Under the Hood
When the LLM decides to call a tool, Embabel executes the following flow:
- Request Parsing – The LLM sends a tool call request containing the tool name and JSON arguments. Embabel receives this via the chat API.
- Definition Lookup – The runtime queries the
ToolGroupMetadatato locate the matchingToolDefinitionby tool name. - Parameter Deserialization – Embabel deserializes the input JSON into Java objects using the parameter schema derived from the method signature (or the explicit
inputSchemaprovided in the@Toolannotation). - Method Execution – The framework invokes the original Java method on the registered Spring bean with the deserialized arguments.
- Response Serialization – The return value is serialized to JSON according to the return type schema and sent back to the LLM as a tool result.
Throughout this process, the observability layer emits events via ToolCallRequestEvent and ToolCallResponseEvent, defined in embabel-agent-observability/src/main/java/com/embabel/agent/api/event/. These events enable tracing and metrics collection through Micrometer or OpenTelemetry.
Code Examples
Basic Domain Service with @Tool
Create a Spring bean and annotate methods you want to expose:
package com.example.customer;
import com.embabel.agent.api.annotation.Tool;
import org.springframework.stereotype.Service;
@Service
public class CustomerService {
@Tool(description = "Find a customer by its unique identifier")
public Customer findById(Long id) {
// Domain logic to retrieve customer
return customerRepository.findById(id)
.orElseThrow(() -> new CustomerNotFoundException(id));
}
@Tool(description = "Create a new customer record")
public Customer create(String name, String email) {
Customer customer = new Customer(name, email);
return customerRepository.save(customer);
}
}
The @Tool annotation creates a ToolDefinition for each method, defaulting the tool name to <ClassName>.<methodName>.
Customizing Tool Names and Schemas
Override default behavior using annotation attributes:
@Tool(
name = "searchCustomers",
description = "Search customers using a free-text query",
inputSchema = """
{
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search terms"},
"limit": {"type": "integer", "default": 10}
},
"required": ["query"]
}
"""
)
public List<Customer> search(String query, int limit) {
return customerRepository.search(query, limit);
}
Explicit inputSchema overrides the automatically generated JSON Schema, useful for complex validation rules or documentation.
Handling Tool Calls in Chat Sessions
The Embabel runtime automatically handles tool invocations during chat processing:
ChatRequest request = ChatRequest.builder()
.message("Find the customer with ID 42 and update their email")
.build();
ChatResponse response = embabelAgent.chat(request);
// Embabel automatically invokes CustomerService.findById(42)
// and any subsequent tool calls required to complete the request
You do not manually call the tool methods; the LLM requests them by name, and Embabel routes the invocation through the ToolRegistry to your domain bean.
Observability and Tool Events
Every tool invocation generates observability events defined in the embabel-agent-observability module:
ToolCallRequestEvent(embabel-agent-observability/src/main/java/com/embabel/agent/api/event/ToolCallRequestEvent.java) – Emitted when the LLM requests a tool call, containing the tool name and input parameters.ToolCallResponseEvent(embabel-agent-observability/src/main/java/com/embabel/agent/api/event/ToolCallResponseEvent.java) – Emitted after the method returns, containing the result or error details.
These events integrate with EmbabelToolLoopObservationConvention to produce metrics such as embabel.tool.calls.total and embabel.tool.duration. You can consume these events to build dashboards monitoring which domain methods your LLM invokes most frequently.
Summary
- Annotate domain methods with
@Toolfromcom.embabel.agent.api.annotation.Toolto mark them as LLM-callable. - Register the containing class as a Spring bean;
ToolRegistryautomatically discovers and indexes annotated methods. - Metadata is stored in
ToolGroupMetadataobjects that bundleToolDefinitioninstances for runtime lookup. - Invocation flows through Embabel's tool-calling API, which handles JSON serialization, method routing, and response formatting.
- Monitor tool usage via
ToolCallRequestEventandToolCallResponseEventfor complete observability of LLM-domain interactions.
Frequently Asked Questions
What annotation is used to expose methods as LLM tools in Embabel?
Use the @Tool annotation from the embabel-agent-api module. Apply it to any public method in a Spring bean to generate a ToolDefinition. The annotation supports attributes like name, description, and inputSchema to customize how the LLM perceives the tool.
How does Embabel generate JSON schemas for tool parameters?
By default, Embabel introspects the method signature at runtime to derive parameter types and constructs a JSON Schema automatically. You can override this by providing an explicit inputSchema string in the @Tool annotation, which is useful for adding descriptions, validation patterns, or default values.
Where are tool definitions stored during runtime?
Tool definitions are stored in ToolGroupMetadata instances managed by the ToolRegistry. Each domain bean with @Tool methods gets one ToolGroupMetadata object containing all its ToolDefinition instances, stored in embabel-agent-observability/src/main/java/com/embabel/agent/core/ToolGroupMetadata.java.
How can I monitor tool usage in Embabel?
Embabel emits ToolCallRequestEvent and ToolCallResponseEvent events before and after each tool invocation. You can listen for these events or use the built-in Micrometer integration via EmbabelToolLoopObservationConvention to record metrics like call counts and execution duration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →