# How to Expose Domain Object Methods as LLM Tools in Embabel

> Learn how to expose domain object methods as LLM tools in Embabel. Annotate methods with @Tool and let ToolRegistry bundle them for your LLM runtime. Simplify agent development.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: how-to-guide
- Published: 2026-08-09

---

**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`](https://github.com/embabel/embabel-agent/blob/main/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`](https://github.com/embabel/embabel-agent/blob/main/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`](https://github.com/embabel/embabel-agent/blob/main/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:

1. **Request Parsing** – The LLM sends a tool call request containing the tool name and JSON arguments. Embabel receives this via the chat API.
2. **Definition Lookup** – The runtime queries the `ToolGroupMetadata` to locate the matching `ToolDefinition` by tool name.
3. **Parameter Deserialization** – Embabel deserializes the input JSON into Java objects using the parameter schema derived from the method signature (or the explicit `inputSchema` provided in the `@Tool` annotation).
4. **Method Execution** – The framework invokes the original Java method on the registered Spring bean with the deserialized arguments.
5. **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:

```java
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:

```java
@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:

```java
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`](https://github.com/embabel/embabel-agent/blob/main/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`](https://github.com/embabel/embabel-agent/blob/main/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 `@Tool` from `com.embabel.agent.api.annotation.Tool` to mark them as LLM-callable.
- **Register** the containing class as a Spring bean; `ToolRegistry` automatically discovers and indexes annotated methods.
- **Metadata** is stored in `ToolGroupMetadata` objects that bundle `ToolDefinition` instances for runtime lookup.
- **Invocation** flows through Embabel's tool-calling API, which handles JSON serialization, method routing, and response formatting.
- **Monitor** tool usage via `ToolCallRequestEvent` and `ToolCallResponseEvent` for 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`](https://github.com/embabel/embabel-agent/blob/main/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.