Tools

The assistant does not access the database or external systems directly. Instead, it works through server-side tools. The add-on includes a set of predefined tools, and you can contribute your own or override the predefined ones.

Predefined Tools

The assistant comes with predefined tools that it can call on the user’s behalf. They are grouped by purpose, and each group is described in its own section below. At the moment, the add-on provides one group: Data Load tools.

Data Load Tools

The Data Load tools let the assistant answer questions about application data.

The Data-Load Flow

The Data Load tools implement a natural-language data loading flow. When a user asks for data, the assistant typically goes through the following steps:

  1. Discover – list the entities available to the user and inspect the relevant ones, including attributes, relationships, and enums.

  2. Generate – produce a read-only JPQL query with named parameters for the request.

  3. Validate – check the query against the domain model and a set of rules, including read-only access, syntax, known entities and attributes, supported date/time constructs, and pagination.

  4. Repair – if validation fails, ask the model to fix the query and validate it again, up to a configurable number of attempts.

  5. Execute – run the query through DataManager, enforce entity and attribute permissions, and return a page of rows.

All steps run on the server. The query is always a read-only SELECT. Write operations and native SQL escapes are rejected during validation, and execution also enforces the current user’s read permissions. A query over an entity the user cannot read is rejected, while attributes the user cannot read are omitted from the returned rows, just as a data grid hides columns bound to unreadable attributes. In both cases, the user never receives data they are not allowed to see.

Available Tools

The flow is exposed to the model as three tools. The tool name is the identifier the model uses to call the tool. It is also the name you reference when overriding a tool.

Tool name What it does

aitls_getAvailableEntities

Returns compact metadata for every entity currently available to the user: entity name, localized names, and property names. Entities hidden by application filtering or security are not returned. The assistant uses this tool to explore the data model and obtain the correct entity names for follow-up calls.

aitls_getDomainModelForEntities

Returns detailed metadata for the requested entities: exact attribute names, relationships for joins, property types and constraints, and enum value mappings. The assistant must call this tool for the entities it intends to query before it generates a query.

aitls_executeQuery

Validates, repairs if needed, and runs a read-only JPQL query, returning the resulting rows. Parameters are passed as a structured request containing query text, named parameters, result column names, and paging information. The result reports the rows, whether more rows are available, and any validation or execution error.

These three tools are bound together by the DataLoadAiTool marker interface, so the registry can collect them as a group. See AiToolRegistry.

Controlling the Exposed Model

By default, the assistant can see every JPA entity in the application except framework entities, because the io.jmix packages are excluded, and system-level entities. You can narrow or widen this set with the jmix.aitools.dataload.* properties.

For example, to hide a specific entity from the assistant and cap the default number of rows per query:

# Hide the User entity from the AI
jmix.aitools.dataload.exclude-entities[0]=User
# Default number of rows returned when a generated query does not specify one
jmix.aitools.dataload.jpql-execution-max-result=50

Inclusion and exclusion rules are evaluated per entity. The exclude- properties hide entities. The include- properties are additive and add entities to the default set, including entities hidden by default, such as framework or system-level entities. exclude-entities still takes precedence over any include. See Data-load properties for the full list of options, including how to cap the number of rows that a single query can return.

Filtering by configuration changes only which entities are offered to the model. Regardless of configuration, every query still runs under the current user’s data access permissions. A query over an entity the user cannot read is rejected, and attributes the user cannot read are removed from the result. A user can never read data they are not allowed to see.

Customizing Availability

The set of entities offered to the model is resolved through an AvailableEntityFilter bean. The default implementation hides entities for which the current user has no read access. To change this behavior, for example to apply your own visibility rules, register your own bean that implements io.jmix.aitools.dataload.introspection.AvailableEntityFilter.

The configured entities and their metadata are served by the AvailableEntityService bean, which you can also use from your own tools. See the override example.

Custom Tools and the Registry

Beyond the predefined tools, you can contribute your own tools to the assistant and override the tools provided by the add-on.

Defining a Tool

A tool is a Spring bean that:

  • implements the io.jmix.aitools.tool.JmixAiTool marker interface

  • declares one or more methods annotated with Spring AI’s @Tool annotation

The add-on discovers all such beans at startup, collects their @Tool methods, and makes them available to the assistant. The following bean adds a tool that returns the catalog of onboarding steps:

@Component
public class OnboardingTools implements JmixAiTool { (1)

    @Autowired
    private DataManager dataManager;
    @Autowired
    private AiToolStatusPublisher statusPublisher;

    @Tool(name = "getStepCatalog", (2)
            description = "Returns the catalog of onboarding steps with their duration in days.")
    public String getStepCatalog(ToolContext toolContext) { (3)
        String message = "Loading the onboarding step catalog";
        statusPublisher.update(message, toolContext); (4)

        List<Step> steps = dataManager.load(Step.class).all().list(); (5)

        statusPublisher.complete(message, steps.size() + " steps", toolContext);

        return steps.stream()
                .sorted(Comparator.comparing(Step::getSortValue))
                .map(step -> step.getName() + " — " + step.getDuration() + " day(s)")
                .collect(Collectors.joining("\n"));
    }
}
1 Implementing JmixAiTool marks the bean as a source of tools.
2 The @Tool name and description are what the model sees. The description should explain when and how to use the tool.
3 A ToolContext parameter is supplied by the framework and is not exposed to the model. It is required only to publish status updates. See Publishing Status Updates.
4 Publishes an in-progress status update before the long-running work starts.
5 Loads the Step entities through DataManager under the current user’s data access permissions.

The method parameters become the tool’s input schema. Annotate them with @ToolParam to add descriptions. The return value is sent back to the model as the tool result.

Declaring @Tool methods requires Spring AI’s model API on the application classpath. The Spring AI model starter that you add in Connecting a Model already provides it.

AiToolRegistry

io.jmix.aitools.tool.AiToolRegistry is the central registry of all tools available in the application. It is built once at startup from every JmixAiTool bean, after override resolution is applied. Its methods are:

Method Description

getAll()

Returns all resolved tools in registration order.

findByName(String name)

Returns the tool registered under the given name, or an empty result.

findByMarker(Class<? extends JmixAiTool> marker)

Returns the tools whose source bean implements the given marker sub-interface of JmixAiTool, for example DataLoadAiTool.class.

getAllCallbacks()

Returns the Spring AI ToolCallback objects of all resolved tools, ready to pass to a ChatClient.

The assistant passes getAllCallbacks() to the model, so any tool you contribute becomes available automatically. Nothing else must be registered.

Overriding a Predefined Tool

To replace an existing tool, including a predefined one, annotate your @Tool method with @ToolOverride and pass the name of the tool you want to replace. The method must still carry @Tool and be declared on a JmixAiTool bean.

The following bean overrides aitls_getAvailableEntities and returns the available entities ordered by their localized name:

@Component
public class SortedEntitiesTool implements DataLoadAiTool { (1)

    @Autowired
    private AvailableEntityService availableEntityService;

    @Tool(description = "Returns entities available to the user, ordered by localized name.")
    @ToolOverride("aitls_getAvailableEntities") (2)
    public List<EntitySummary> getAvailableEntities() {
        return availableEntityService.getEntitySummaries().stream() (3)
                .sorted(Comparator.comparing(this::firstLocalizedName))
                .toList();
    }

    private String firstLocalizedName(EntitySummary summary) {
        return summary.getLocalizedNames().isEmpty()
                ? summary.getEntityName()
                : summary.getLocalizedNames().get(0);
    }
}
1 Implementing DataLoadAiTool keeps the override in the data-load tool group. For a tool unrelated to data loading, implement JmixAiTool directly.
2 @ToolOverride names the tool being replaced. The override is exposed under that name, so the override method’s own @Tool name is irrelevant.
3 The default AvailableEntityService is reused, so the override still honors entity filtering and security and changes only the ordering.

How override resolution works:

  • When several @Tool methods produce the same tool name, the one annotated with @ToolOverride wins and the original tool is excluded from the registry.

  • The override inherits the marker interfaces of the tool it replaces, so registry lookups by marker continue to work.

  • If the named tool does not exist, a warning is logged and the override is registered as a regular new tool under its own @Tool name. This fails with an exception if that name is already used by another tool, to avoid silently replacing an unrelated tool.

Publishing Status Updates

Long-running tools can surface progress to the UI through the io.jmix.aitools.tool.AiToolStatusPublisher bean shown in the example above. It implements a two-phase contract:

  • update(message, toolContext) – the step has started, and the UI shows an in-progress indicator.

  • complete(message, snippet, toolContext) – the step has finished, and the UI folds the result snippet into the same entry. The message must match the one passed to update.

Both methods take the tool method’s ToolContext. When the tool is invoked outside the chat UI, for example through the programmatic API, no status callback is present and the methods are silent no-ops, so it is always safe to call them.